mealy: improve error reporting

* spot/twaalgos/mealy_machine.cc: Add more exceptions.
* tests/python/except.py: Test them.
This commit is contained in:
Alexandre Duret-Lutz 2022-11-04 18:20:29 +01:00
parent fafe40c530
commit 5c5133348e
2 changed files with 86 additions and 28 deletions

View file

@ -100,7 +100,8 @@ namespace
, f{std::fopen(name.c_str(), "a")} , f{std::fopen(name.c_str(), "a")}
{ {
if (!f) if (!f)
throw std::runtime_error("File could not be oppened for writing."); throw std::runtime_error("`" + name +
"' could not be oppened for writing.");
} }
~fwrapper() ~fwrapper()
{ {
@ -120,21 +121,36 @@ namespace
namespace spot namespace spot
{ {
static bdd
ensure_mealy(const char* function_name,
const const_twa_graph_ptr& m)
{
if (SPOT_UNLIKELY(!m->acc().is_t()))
throw std::runtime_error(std::string(function_name) +
"(): Mealy machines must have "
"true acceptance condition");
bdd* out = m->get_named_prop<bdd>("synthesis-outputs");
if (SPOT_UNLIKELY(!out))
throw std::runtime_error(std::string(function_name) +
"(): \"synthesis-outputs\" not defined");
return *out;
}
bool bool
is_mealy(const const_twa_graph_ptr& m) is_mealy(const const_twa_graph_ptr& m)
{ {
if (!m->acc().is_t()) if (!m->acc().is_t())
{ {
trace << "is_mealy(): Mealy machines must have " trace << "is_mealy(): Mealy machines must have "
"true acceptance condition.\n"; "true acceptance condition.\n";
return false; return false;
} }
if (!m->get_named_prop<bdd>("synthesis-outputs")) if (!m->get_named_prop<bdd>("synthesis-outputs"))
{ {
trace << "is_mealy(): \"synthesis-outputs\" not found!\n"; trace << "is_mealy(): \"synthesis-outputs\" not found!\n";
return false; return false;
} }
return true; return true;
} }
@ -252,9 +268,7 @@ namespace spot
void void
split_separated_mealy_here(const twa_graph_ptr& m) split_separated_mealy_here(const twa_graph_ptr& m)
{ {
assert(is_mealy(m)); bdd output_bdd = ensure_mealy("split_separated_mealy_here", m);
auto output_bdd = get_synthesis_outputs(m);
struct dst_cond_color_t struct dst_cond_color_t
{ {
@ -323,10 +337,10 @@ namespace spot
twa_graph_ptr twa_graph_ptr
split_separated_mealy(const const_twa_graph_ptr& m) split_separated_mealy(const const_twa_graph_ptr& m)
{ {
assert(is_mealy((m))); bdd outputs = ensure_mealy("split_separated_mealy", m);
auto m2 = make_twa_graph(m, twa::prop_set::all()); auto m2 = make_twa_graph(m, twa::prop_set::all());
m2->copy_acceptance_of(m); m2->copy_acceptance_of(m);
set_synthesis_outputs(m2, get_synthesis_outputs(m)); set_synthesis_outputs(m2, outputs);
split_separated_mealy_here(m2); split_separated_mealy_here(m2);
return m2; return m2;
} }
@ -767,7 +781,7 @@ namespace spot
twa_graph_ptr reduce_mealy(const const_twa_graph_ptr& mm, twa_graph_ptr reduce_mealy(const const_twa_graph_ptr& mm,
bool output_assignment) bool output_assignment)
{ {
assert(is_mealy(mm)); bdd outputs = ensure_mealy("reduce_mealy", mm);
if (mm->get_named_prop<std::vector<bool>>("state-player")) if (mm->get_named_prop<std::vector<bool>>("state-player"))
throw std::runtime_error("reduce_mealy(): " throw std::runtime_error("reduce_mealy(): "
"Only works on unsplit machines.\n"); "Only works on unsplit machines.\n");
@ -775,7 +789,7 @@ namespace spot
auto mmc = make_twa_graph(mm, twa::prop_set::all()); auto mmc = make_twa_graph(mm, twa::prop_set::all());
mmc->copy_ap_of(mm); mmc->copy_ap_of(mm);
mmc->copy_acceptance_of(mm); mmc->copy_acceptance_of(mm);
set_synthesis_outputs(mmc, get_synthesis_outputs(mm)); set_synthesis_outputs(mmc, outputs);
reduce_mealy_here(mmc, output_assignment); reduce_mealy_here(mmc, output_assignment);
@ -785,7 +799,7 @@ namespace spot
void reduce_mealy_here(twa_graph_ptr& mm, bool output_assignment) void reduce_mealy_here(twa_graph_ptr& mm, bool output_assignment)
{ {
assert(is_mealy(mm)); ensure_mealy("reduce_mealy_here", mm);
// Only consider infinite runs // Only consider infinite runs
mm->purge_dead_states(); mm->purge_dead_states();
@ -902,6 +916,8 @@ namespace
// Writing also "flushes" // Writing also "flushes"
void write() void write()
{ {
if (!sat_csv_file)
return;
auto f = [](std::ostream& o, auto& v, bool sep = true) auto f = [](std::ostream& o, auto& v, bool sep = true)
{ {
if (v >= 0) if (v >= 0)
@ -910,8 +926,6 @@ namespace
o.put(','); o.put(',');
v = -1; v = -1;
}; };
if (!sat_csv_file)
return;
auto& out = *sat_csv_file; auto& out = *sat_csv_file;
if (out.tellp() == 0) if (out.tellp() == 0)
@ -3786,7 +3800,7 @@ namespace spot
twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm,
int premin) int premin)
{ {
assert(is_mealy(mm)); bdd outputs = ensure_mealy("minimize_mealy", mm);
satprob_info si(sat_instance_name); satprob_info si(sat_instance_name);
si.task = "presat"; si.task = "presat";
@ -3916,7 +3930,7 @@ namespace spot
// Set state players! // Set state players!
if (!minmachine) if (!minmachine)
return early_exit(); return early_exit();
set_synthesis_outputs(minmachine, get_synthesis_outputs(mm)); set_synthesis_outputs(minmachine, outputs);
si.done=1; si.done=1;
si.n_min_states = minmachine->num_states(); si.n_min_states = minmachine->num_states();
@ -3931,17 +3945,23 @@ namespace spot
minimize_mealy(const const_twa_graph_ptr& mm, minimize_mealy(const const_twa_graph_ptr& mm,
synthesis_info& si) synthesis_info& si)
{ {
if ((si.minimize_lvl < 3) || (5 < si.minimize_lvl)) if ((si.minimize_lvl < 3) || (si.minimize_lvl > 5))
throw std::runtime_error("Invalid option"); throw std::runtime_error("minimize_mealy(): "
"minimize_lvl should be between 3 and 5.");
std::string csvfile = si.opt.get_str("satlogcsv"); std::string csvfile = si.opt.get_str("satlogcsv");
std::string dimacsfile = si.opt.get_str("satlogdimacs"); std::string dimacsfile = si.opt.get_str("satlogdimacs");
if (!csvfile.empty()) if (!csvfile.empty())
sat_csv_file {
= std::make_unique<std::ofstream>(csvfile, sat_csv_file = std::make_unique<std::ofstream>
std::ios_base::ate (csvfile, std::ios_base::ate | std::ios_base::app);
| std::ios_base::app); if (!*sat_csv_file)
throw std::runtime_error("could not open `" + csvfile
+ "' for writing");
sat_csv_file->exceptions(std::ofstream::failbit
| std::ofstream::badbit);
}
if (!dimacsfile.empty()) if (!dimacsfile.empty())
sat_dimacs_file sat_dimacs_file
= std::make_unique<fwrapper>(dimacsfile); = std::make_unique<fwrapper>(dimacsfile);

View file

@ -321,3 +321,41 @@ except RuntimeError as e:
tc.assertIn("already registered", se) tc.assertIn("already registered", se)
else: else:
report_missing_exception() report_missing_exception()
try:
spot.minimize_mealy(a, 100)
except RuntimeError as e:
se = str(e)
tc.assertIn("minimize_mealy", se)
tc.assertIn("minimize_lvl", se)
else:
report_missing_exception()
opt = spot.synthesis_info()
opt.minimize_lvl = 3
try:
spot.minimize_mealy(a, opt)
except RuntimeError as e:
se = str(e)
tc.assertIn("minimize_mealy", se)
tc.assertIn("synthesis-output", se)
spot.set_synthesis_outputs(a, buddy.bdd_ithvar(a.register_ap("b")))
filename = "/THIS-FILE/SHOULD/NOT/EXIST"
opt.opt.set_str("satlogdimacs", filename)
try:
spot.minimize_mealy(a, opt)
except RuntimeError as e:
tc.assertIn(filename, str(e))
else:
report_missing_exception()
opt.opt.set_str("satlogdimacs", "")
opt.opt.set_str("satlogcsv", filename)
try:
spot.minimize_mealy(a, opt)
except RuntimeError as e:
tc.assertIn(filename, str(e))
else:
report_missing_exception()