spot/src/tgbaalgos/dtgbasat.cc
Alexandre Duret-Lutz edb220af6a dtbasat,dtgbasat: rewrite using the tgba_digraph interface
This gets rid of many state*/int conversions.  We now use scc_info
instead of scc_map.  Finally the loops are now all 0-based.

* src/tgbaalgos/sccinfo.cc, src/tgbaalgos/sccinfo.hh (weak_sccs): New
method.
* src/tgbaalgos/dtbasat.cc, src/tgbaalgos/dtbasat.hh,
src/tgbaalgos/dtgbasat.cc, src/tgbaalgos/dtgbasat.hh: Use the
tgba_digraph interface.
* src/tgbatest/ltl2tgba.cc: Adjust calls.
2014-08-20 16:18:35 +02:00

1024 lines
25 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement
// de l'Epita.
//
// 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 <iostream>
#include <fstream>
#include <sstream>
#include "dtgbasat.hh"
#include "reachiter.hh"
#include <map>
#include <utility>
#include "sccinfo.hh"
#include "tgba/bddprint.hh"
#include "ltlast/constant.hh"
#include "stats.hh"
#include "ltlenv/defaultenv.hh"
#include "misc/satsolver.hh"
#include "misc/timer.hh"
#include "isweakscc.hh"
#include "dotty.hh"
// If you set the SPOT_TMPKEEP environment variable the temporary
// file used to communicate with the sat solver will be left in
// the current directory.
//
// Additionally, if the following DEBUG macro is set to 1, the CNF
// file will be output with a comment before each clause, and an
// additional output file (dtgba-sat.dbg) will be created with a list
// of all positive variables in the result and their meaning.
#define DEBUG 0
#if DEBUG
#define dout out << "c "
#define trace std::cerr
#else
#define dout while (0) std::cout
#define trace dout
#endif
namespace spot
{
namespace
{
static bdd_dict_ptr debug_dict = 0;
struct transition
{
unsigned src;
bdd cond;
unsigned dst;
transition(int src, bdd cond, int dst)
: src(src), cond(cond), dst(dst)
{
}
bool operator<(const transition& other) const
{
if (this->src < other.src)
return true;
if (this->src > other.src)
return false;
if (this->dst < other.dst)
return true;
if (this->dst > other.dst)
return false;
return this->cond.id() < other.cond.id();
}
bool operator==(const transition& other) const
{
return (this->src == other.src
&& this->dst == other.dst
&& this->cond.id() == other.cond.id());
}
};
struct src_cond
{
unsigned src;
bdd cond;
src_cond(int src, bdd cond)
: src(src), cond(cond)
{
}
bool operator<(const src_cond& other) const
{
if (this->src < other.src)
return true;
if (this->src > other.src)
return false;
return this->cond.id() < other.cond.id();
}
bool operator==(const src_cond& other) const
{
return (this->src == other.src
&& this->cond.id() == other.cond.id());
}
};
struct transition_acc
{
unsigned src;
bdd cond;
bdd acc;
unsigned dst;
transition_acc(int src, bdd cond, bdd acc, int dst)
: src(src), cond(cond), acc(acc), dst(dst)
{
}
bool operator<(const transition_acc& other) const
{
if (this->src < other.src)
return true;
if (this->src > other.src)
return false;
if (this->dst < other.dst)
return true;
if (this->dst > other.dst)
return false;
if (this->cond.id() < other.cond.id())
return true;
if (this->cond.id() > other.cond.id())
return false;
return this->acc.id() < other.acc.id();
}
bool operator==(const transition_acc& other) const
{
return (this->src == other.src
&& this->dst == other.dst
&& this->cond.id() == other.cond.id()
&& this->acc.id() == other.acc.id());
}
};
struct path
{
unsigned src_cand;
unsigned src_ref;
unsigned dst_cand;
unsigned dst_ref;
bdd acc_cand;
bdd acc_ref;
path(unsigned src_cand, unsigned src_ref)
: src_cand(src_cand), src_ref(src_ref),
dst_cand(src_cand), dst_ref(src_ref),
acc_cand(bddfalse), acc_ref(bddfalse)
{
}
path(unsigned src_cand, unsigned src_ref,
unsigned dst_cand, unsigned dst_ref,
bdd acc_cand, bdd acc_ref)
: src_cand(src_cand), src_ref(src_ref),
dst_cand(dst_cand), dst_ref(dst_ref),
acc_cand(acc_cand), acc_ref(acc_ref)
{
}
bool operator<(const path& other) const
{
if (this->src_cand < other.src_cand)
return true;
if (this->src_cand > other.src_cand)
return false;
if (this->src_ref < other.src_ref)
return true;
if (this->src_ref > other.src_ref)
return false;
if (this->dst_cand < other.dst_cand)
return true;
if (this->dst_cand > other.dst_cand)
return false;
if (this->dst_ref < other.dst_ref)
return true;
if (this->dst_ref > other.dst_ref)
return false;
if (this->acc_ref.id() < other.acc_ref.id())
return true;
if (this->acc_ref.id() > other.acc_ref.id())
return false;
if (this->acc_cand.id() < other.acc_cand.id())
return true;
if (this->acc_cand.id() > other.acc_cand.id())
return false;
return false;
}
};
std::ostream& operator<<(std::ostream& os, const transition& t)
{
os << '<' << t.src << ','
<< bdd_format_formula(debug_dict, t.cond)
<< ',' << t.dst << '>';
return os;
}
std::ostream& operator<<(std::ostream& os, const transition_acc& t)
{
os << '<' << t.src << ','
<< bdd_format_formula(debug_dict, t.cond) << ','
<< bdd_format_accset(debug_dict, t.acc)
<< ',' << t.dst << '>';
return os;
}
std::ostream& operator<<(std::ostream& os, const path& p)
{
os << '<'
<< p.src_cand << ','
<< p.src_ref << ','
<< p.dst_cand << ','
<< p.dst_ref << ", "
<< bdd_format_accset(debug_dict, p.acc_cand) << ", "
<< bdd_format_accset(debug_dict, p.acc_ref) << '>';
return os;
}
struct dict
{
dict(const const_tgba_ptr& a)
: aut(a)
{
}
const_tgba_ptr aut;
typedef std::map<transition, int> trans_map;
typedef std::map<transition_acc, int> trans_acc_map;
trans_map transid;
trans_acc_map transaccid;
typedef std::map<int, transition> rev_map;
typedef std::map<int, transition_acc> rev_acc_map;
rev_map revtransid;
rev_acc_map revtransaccid;
std::map<path, int> pathid;
int nvars = 0;
//typedef std::unordered_map<const state*, int,
//state_ptr_hash, state_ptr_equal> state_map;
//typedef std::unordered_map<int, const state*> int_map;
//state_map state_to_int;
// int_map int_to_state;
unsigned cand_size;
unsigned int cand_nacc;
std::vector<bdd> cand_acc; // size cand_nacc
std::vector<bdd> all_cand_acc;
std::vector<bdd> all_ref_acc;
bdd cand_all_acc;
bdd ref_all_acc;
std::vector<bool> is_weak_scc;
~dict()
{
aut->get_dict()->unregister_all_my_variables(this);
}
};
unsigned declare_vars(const const_tgba_digraph_ptr& aut,
dict& d, bdd ap, bool state_based, scc_info& sm)
{
bdd_dict_ptr bd = aut->get_dict();
ltl::default_environment& env = ltl::default_environment::instance();
d.cand_acc.resize(d.cand_nacc);
d.all_cand_acc.push_back(bddfalse);
bdd allneg = bddtrue;
for (unsigned n = 0; n < d.cand_nacc; ++n)
{
std::ostringstream s;
s << n;
const ltl::formula* af = env.require(s.str());
int v = bd->register_acceptance_variable(af, &d);
af->destroy();
d.cand_acc[n] = bdd_ithvar(v);
allneg &= bdd_nithvar(v);
}
for (unsigned n = 0; n < d.cand_nacc; ++n)
{
bdd c = bdd_exist(allneg, d.cand_acc[n]) & d.cand_acc[n];
d.cand_acc[n] = c;
size_t s = d.all_cand_acc.size();
for (size_t i = 0; i < s; ++i)
d.all_cand_acc.push_back(d.all_cand_acc[i] | c);
}
d.cand_all_acc = bdd_support(allneg);
d.ref_all_acc = bdd_support(aut->all_acceptance_conditions());
bdd refall = d.ref_all_acc;
bdd refnegall = aut->neg_acceptance_conditions();
d.all_ref_acc.push_back(bddfalse);
while (refall != bddtrue)
{
bdd v = bdd_ithvar(bdd_var(refall));
bdd c = bdd_exist(refnegall, v) & v;
size_t s = d.all_ref_acc.size();
for (size_t i = 0; i < s; ++i)
d.all_ref_acc.push_back(d.all_ref_acc[i] | c);
refall = bdd_high(refall);
}
unsigned ref_size = aut->num_states();
if (d.cand_size == -1U)
for (unsigned i = 0; i < ref_size; ++i)
if (sm.reachable_state(i))
++d.cand_size; // Note that we start from -1U the
// cand_size is one less than the
// number of reachable states.
for (unsigned i = 0; i < ref_size; ++i)
{
if (!sm.reachable_state(i))
continue;
unsigned i_scc = sm.scc_of(i);
bool is_weak = d.is_weak_scc[i_scc];
for (unsigned j = 0; j < d.cand_size; ++j)
{
for (unsigned k = 0; k < ref_size; ++k)
{
if (!sm.reachable_state(k))
continue;
if (sm.scc_of(k) != i_scc)
continue;
for (unsigned l = 0; l < d.cand_size; ++l)
{
size_t sfp = is_weak ? 1 : d.all_ref_acc.size();
for (size_t fp = 0; fp < sfp; ++fp)
{
size_t sf = d.all_cand_acc.size();
for (size_t f = 0; f < sf; ++f)
{
path p(j, i, l, k,
d.all_cand_acc[f],
d.all_ref_acc[fp]);
d.pathid[p] = ++d.nvars;
}
}
}
}
}
}
if (!state_based)
{
for (unsigned i = 0; i < d.cand_size; ++i)
for (unsigned j = 0; j < d.cand_size; ++j)
{
bdd all = bddtrue;
while (all != bddfalse)
{
bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one;
transition t(i, one, j);
d.transid[t] = ++d.nvars;
d.revtransid.emplace(d.nvars, t);
// Create the variable for the accepting transition
// immediately afterwards. It helps parsing the
// result.
for (unsigned n = 0; n < d.cand_nacc; ++n)
{
transition_acc ta(i, one, d.cand_acc[n], j);
d.transaccid[ta] = ++d.nvars;
d.revtransaccid.emplace(d.nvars, ta);
}
}
}
}
else // state based
{
for (unsigned i = 0; i < d.cand_size; ++i)
for (unsigned n = 0; n < d.cand_nacc; ++n)
{
++d.nvars;
for (unsigned j = 1; j < d.cand_size; ++j)
{
bdd all = bddtrue;
while (all != bddfalse)
{
bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one;
transition_acc ta(i, one, d.cand_acc[n], j);
d.transaccid[ta] = d.nvars;
d.revtransaccid.emplace(d.nvars, ta);
}
}
}
for (unsigned i = 0; i < d.cand_size; ++i)
for (unsigned j = 0; j < d.cand_size; ++j)
{
bdd all = bddtrue;
while (all != bddfalse)
{
bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one;
transition t(i, one, j);
d.transid[t] = ++d.nvars;
d.revtransid.emplace(d.nvars, t);
}
}
}
return ref_size;
}
typedef std::pair<int, int> sat_stats;
static
sat_stats dtgba_to_sat(std::ostream& out, const_tgba_digraph_ptr ref,
dict& d, bool state_based)
{
clause_counter nclauses;
// Compute the AP used in the hard way.
bdd ap = bddtrue;
for (auto& t: ref->transitions())
if (!ref->is_dead_transition(t))
ap &= bdd_support(t.cond);
// Count the number of atomic propositions
int nap = 0;
{
bdd cur = ap;
while (cur != bddtrue)
{
++nap;
cur = bdd_high(cur);
}
nap = 1 << nap;
}
scc_info sm(ref);
d.is_weak_scc = sm.weak_sccs();
// Number all the SAT variables we may need.
unsigned ref_size = declare_vars(ref, d, ap, state_based, sm);
// empty automaton is impossible
if (d.cand_size == 0)
{
out << "p cnf 1 2\n-1 0\n1 0\n";
return std::make_pair(1, 2);
}
// An empty line for the header
out << " \n";
#if DEBUG
debug_dict = ref->get_dict();
dout << "ref_size: " << ref_size << '\n';
dout << "cand_size: " << d.cand_size << '\n';
#endif
dout << "symmetry-breaking clauses\n";
int j = 0;
bdd all = bddtrue;
while (all != bddfalse)
{
bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s;
for (unsigned i = 0; i < d.cand_size - 1; ++i)
for (unsigned k = i * nap + j + 2; k < d.cand_size; ++k)
{
transition t(i, s, k);
int ti = d.transid[t];
dout << "¬" << t << '\n';
out << -ti << " 0\n";
++nclauses;
}
++j;
}
if (!nclauses.nb_clauses())
dout << "(none)\n";
dout << "(8) the candidate automaton is complete\n";
for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
{
bdd all = bddtrue;
while (all != bddfalse)
{
bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s;
#if DEBUG
dout;
for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{
transition t(q1, s, q2);
out << t << "δ";
if (q2 != d.cand_size)
out << " ";
}
out << '\n';
#endif
for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{
transition t(q1, s, q2);
int ti = d.transid[t];
out << ti << ' ';
}
out << "0\n";
++nclauses;
}
}
dout << "(9) the initial state is reachable\n";
dout << path(0, 0) << '\n';
out << d.pathid[path(0, 0)] << " 0\n";
++nclauses;
for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
for (unsigned q1p = 0; q1p < ref_size; ++q1p)
{
if (!sm.reachable_state(q1p))
continue;
dout << "(10) augmenting paths based on Cand[" << q1
<< "] and Ref[" << q1p << "]\n";
path p1(q1, q1p);
int p1id = d.pathid[p1];
for (auto& tr: ref->out(q1p))
{
unsigned dp = tr.dst;
bdd all = tr.cond;
while (all != bddfalse)
{
bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s;
for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{
transition t(q1, s, q2);
int ti = d.transid[t];
path p2(q2, dp);
int succ = d.pathid[p2];
if (p1id == succ)
continue;
dout << p1 << "" << t << "δ → " << p2 << '\n';
out << -p1id << ' ' << -ti << ' ' << succ << " 0\n";
++nclauses;
}
}
}
}
bdd all_acc = ref->all_acceptance_conditions();
// construction of constraints (11,12,13)
for (unsigned q1p = 0; q1p < ref_size; ++q1p)
{
if (!sm.reachable_state(q1p))
continue;
unsigned q1p_scc = sm.scc_of(q1p);
for (unsigned q2p = 0; q2p < ref_size; ++q2p)
{
if (!sm.reachable_state(q2p))
continue;
// We are only interested in transition that can form a
// cycle, so they must belong to the same SCC.
if (sm.scc_of(q2p) != q1p_scc)
continue;
bool is_weak = d.is_weak_scc[q1p_scc];
bool is_acc = sm.is_accepting_scc(q1p_scc);
for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{
size_t sf = d.all_cand_acc.size();
size_t sfp = is_weak ? 1 : d.all_ref_acc.size();
for (size_t f = 0; f < sf; ++f)
for (size_t fp = 0; fp < sfp; ++fp)
{
path p(q1, q1p, q2, q2p,
d.all_cand_acc[f], d.all_ref_acc[fp]);
dout << "(11&12&13) paths from " << p << '\n';
int pid = d.pathid[p];
for (auto& tr: ref->out(q2p))
{
unsigned dp = tr.dst;
// Skip destinations not in the SCC.
if (sm.scc_of(dp) != q1p_scc)
continue;
for (unsigned q3 = 0; q3 < d.cand_size; ++q3)
{
bdd all = tr.cond;
bdd curacc = tr.acc;
while (all != bddfalse)
{
bdd l = bdd_satoneset(all, ap, bddfalse);
all -= l;
transition t(q2, l, q3);
int ti = d.transid[t];
if (dp == q1p && q3 == q1) // (11,12) loop
{
if ((!is_acc) ||
(!is_weak &&
(curacc |
d.all_ref_acc[fp]) != all_acc))
{
#if DEBUG
dout << "(11) " << p << ""
<< t << "δ → ¬(";
bdd all_ = d.all_cand_acc.back();
all_ -= d.all_cand_acc[f];
bool notfirst = false;
while (all_ != bddfalse)
{
bdd one = bdd_satone(all_);
all_ -= one;
transition_acc ta(q2, l,
one, q1);
if (notfirst)
out << "";
else
notfirst = true;
out << ta << "FC";
}
out << ")\n";
#endif // DEBUG
out << -pid << ' ' << -ti;
// 11
bdd all_f = d.all_cand_acc.back();
all_f -= d.all_cand_acc[f];
while (all_f != bddfalse)
{
bdd one = bdd_satone(all_f);
all_f -= one;
transition_acc ta(q2, l,
one, q1);
int tai = d.transaccid[ta];
assert(tai != 0);
out << ' ' << -tai;
}
out << " 0\n";
++nclauses;
}
else
{
#if DEBUG
dout << "(12) " << p << ""
<< t << "δ → (";
bdd all_ = d.all_cand_acc.back();
all_ -= d.all_cand_acc[f];
bool notfirst = false;
while (all_ != bddfalse)
{
bdd one = bdd_satone(all_);
all_ -= one;
transition_acc ta(q2, l,
one, q1);
if (notfirst)
out << "";
else
notfirst = true;
out << ta << "FC";
}
out << ")\n";
#endif // DEBUG
// 12
bdd all_f = d.all_cand_acc.back();
all_f -= d.all_cand_acc[f];
while (all_f != bddfalse)
{
bdd one = bdd_satone(all_f);
all_f -= one;
transition_acc ta(q2, l,
one, q1);
int tai = d.transaccid[ta];
assert(tai != 0);
out << -pid << ' ' << -ti
<< ' ' << tai << " 0\n";
++nclauses;
}
}
}
// (13) augmenting paths (always).
{
size_t sf = d.all_cand_acc.size();
for (size_t f = 0; f < sf; ++f)
{
bdd f2 = p.acc_cand |
d.all_cand_acc[f];
bdd f2p = bddfalse;
if (!is_weak)
f2p = p.acc_ref | curacc;
path p2(p.src_cand, p.src_ref,
q3, dp, f2, f2p);
int p2id = d.pathid[p2];
if (pid == p2id)
continue;
#if DEBUG
dout << "(13) " << p << ""
<< t << "δ ";
bdd biga_ = d.all_cand_acc[f];
while (biga_ != bddfalse)
{
bdd a = bdd_satone(biga_);
biga_ -= a;
transition_acc ta(q2, l, a, q3);
out << "" << ta << "FC";
}
biga_ = d.all_cand_acc.back()
- d.all_cand_acc[f];
while (biga_ != bddfalse)
{
bdd a = bdd_satone(biga_);
biga_ -= a;
transition_acc ta(q2, l, a, q3);
out << " ∧ ¬" << ta << "FC";
}
out << "" << p2 << '\n';
#endif
out << -pid << ' ' << -ti << ' ';
bdd biga = d.all_cand_acc[f];
while (biga != bddfalse)
{
bdd a = bdd_satone(biga);
biga -= a;
transition_acc ta(q2, l, a, q3);
int tai = d.transaccid[ta];
out << -tai << ' ';
}
biga = d.all_cand_acc.back()
- d.all_cand_acc[f];
while (biga != bddfalse)
{
bdd a = bdd_satone(biga);
biga -= a;
transition_acc ta(q2, l, a, q3);
int tai = d.transaccid[ta];
out << tai << ' ';
}
out << p2id << " 0\n";
++nclauses;
}
}
}
}
}
}
}
}
}
out.seekp(0);
out << "p cnf " << d.nvars << ' ' << nclauses.nb_clauses();
return std::make_pair(d.nvars, nclauses.nb_clauses());
}
static tgba_digraph_ptr
sat_build(const satsolver::solution& solution, dict& satdict,
const_tgba_digraph_ptr aut, bool state_based)
{
auto autdict = aut->get_dict();
auto a = make_tgba_digraph(autdict);
a->copy_ap_of(aut);
a->set_acceptance_conditions(satdict.all_cand_acc.back());
a->new_states(satdict.cand_size);
// Last transition set in the automaton.
unsigned last_aut_trans = -1U;
// Last transition read from the SAT result.
const transition* last_sat_trans = nullptr;
#if DEBUG
std::fstream out("dtgba-sat.dbg",
std::ios_base::trunc | std::ios_base::out);
out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::set<int> positive;
#endif
dout << "--- transition variables ---\n";
std::map<int, bdd> state_acc;
std::set<src_cond> seen_trans;
for (int v: solution)
{
if (v < 0) // FIXME: maybe we can have (v < NNN)?
continue;
#if DEBUG
positive.insert(v);
#endif
dict::rev_map::const_iterator t = satdict.revtransid.find(v);
if (t != satdict.revtransid.end())
{
// Skip (s,l,d2) if we have already seen some (s,l,d1).
if (seen_trans.insert(src_cond(t->second.src,
t->second.cond)).second)
{
bdd acc = bddfalse;
if (state_based)
{
auto i = state_acc.find(t->second.src);
if (i != state_acc.end())
acc = i->second;
}
last_aut_trans = a->new_transition(t->second.src,
t->second.dst,
t->second.cond,
acc);
last_sat_trans = &t->second;
dout << v << '\t' << t->second << "δ\n";
}
}
else
{
dict::rev_acc_map::const_iterator ta;
ta = satdict.revtransaccid.find(v);
// This assumes that the sat solvers output variables in
// increasing order.
if (ta != satdict.revtransaccid.end())
{
dout << v << '\t' << ta->second << "F\n";
if (last_sat_trans &&
ta->second.src == last_sat_trans->src &&
ta->second.cond == last_sat_trans->cond &&
ta->second.dst == last_sat_trans->dst)
{
assert(!state_based);
a->trans_data(last_aut_trans).acc |= ta->second.acc;
}
else if (state_based)
{
state_acc[ta->second.src] |= ta->second.acc;
}
}
}
}
#if DEBUG
dout << "--- pathid variables ---\n";
for (auto pit: satdict.pathid)
if (positive.find(pit.second) != positive.end())
dout << pit.second << '\t' << pit.first << "C\n";
#endif
a->merge_transitions();
return a;
}
}
tgba_digraph_ptr
dtgba_sat_synthetize(const const_tgba_digraph_ptr& a,
unsigned target_acc_number,
int target_state_number, bool state_based)
{
if (target_state_number == 0)
return nullptr;
trace << "dtgba_sat_synthetize(..., acc = " << target_acc_number
<< ", states = " << target_state_number
<< ", state_based = " << state_based << ")\n";
dict d(a);
d.cand_size = target_state_number;
d.cand_nacc = target_acc_number;
satsolver solver;
satsolver::solution_pair solution;
timer_map t;
t.start("encode");
sat_stats s = dtgba_to_sat(solver(), a, d, state_based);
t.stop("encode");
t.start("solve");
solution = solver.get_solution();
t.stop("solve");
tgba_digraph_ptr res = nullptr;
if (!solution.second.empty())
res = sat_build(solution.second, d, a, state_based);
static const char* log = getenv("SPOT_SATLOG");
if (log)
{
std::fstream out(log,
std::ios_base::app | std::ios_base::out);
out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
const timer& te = t.timer("encode");
const timer& ts = t.timer("solve");
out << target_state_number << ',';
if (res)
{
tgba_sub_statistics st = sub_stats_reachable(res);
out << st.states << ',' << st.transitions
<< ',' << st.sub_transitions;
}
else
{
out << ",,";
}
out << ','
<< s.first << ',' << s.second << ','
<< te.utime() << ',' << te.stime() << ','
<< ts.utime() << ',' << ts.stime() << '\n';
}
static const char* show = getenv("SPOT_SATSHOW");
if (show && res)
dotty_reachable(std::cout, res);
trace << "dtgba_sat_synthetize(...) = " << res << '\n';
return res;
}
tgba_digraph_ptr
dtgba_sat_minimize(const const_tgba_digraph_ptr& a,
unsigned target_acc_number,
bool state_based)
{
int n_states = stats_reachable(a).states;
tgba_digraph_ptr prev = nullptr;
for (;;)
{
auto next =
dtgba_sat_synthetize(prev ? prev : a, target_acc_number,
--n_states, state_based);
if (!next)
return prev;
else
n_states = stats_reachable(next).states;
prev = next;
}
SPOT_UNREACHABLE();
}
tgba_digraph_ptr
dtgba_sat_minimize_dichotomy(const const_tgba_digraph_ptr& a,
unsigned target_acc_number,
bool state_based)
{
int max_states = stats_reachable(a).states - 1;
int min_states = 1;
tgba_digraph_ptr prev = nullptr;
while (min_states <= max_states)
{
int target = (max_states + min_states) / 2;
auto next =
dtgba_sat_synthetize(prev ? prev : a, target_acc_number, target,
state_based);
if (!next)
{
min_states = target + 1;
}
else
{
prev = next;
max_states = stats_reachable(next).states - 1;
}
}
return prev;
}
}