spot/src/twaalgos/dtgbasat.cc
Alexandre Duret-Lutz de529df59f rename src/tgbaalgos/ as src/twaalgos/
Automatic mass renaming.

* src/tgbaalgos/: Rename as...
* src/twaalgos/: ... this.
* README, configure.ac, iface/ltsmin/modelcheck.cc, src/Makefile.am,
src/bin/autfilt.cc, src/bin/common_aoutput.cc,
src/bin/common_aoutput.hh, src/bin/common_output.hh,
src/bin/common_post.hh, src/bin/dstar2tgba.cc, src/bin/ltl2tgba.cc,
src/bin/ltl2tgta.cc, src/bin/ltlcross.cc, src/bin/ltldo.cc,
src/bin/ltlfilt.cc, src/bin/randaut.cc, src/dstarparse/dra2ba.cc,
src/dstarparse/nra2nba.cc, src/dstarparse/nsa2tgba.cc,
src/graphtest/twagraph.cc, src/kripke/kripkeprint.cc,
src/ltlvisit/contain.cc, src/ltlvisit/contain.hh,
src/ltlvisit/exclusive.cc, src/taalgos/emptinessta.hh,
src/tgbatest/checkpsl.cc, src/tgbatest/checkta.cc,
src/tgbatest/complementation.cc, src/tgbatest/emptchk.cc,
src/tgbatest/ltl2tgba.cc, src/tgbatest/ltlprod.cc,
src/tgbatest/randtgba.cc, src/tgbatest/taatgba.cc, src/twa/twa.cc,
src/twa/twagraph.hh, src/twa/twasafracomplement.cc,
wrap/python/spot_impl.i: Adjust.
2015-04-22 17:58:06 +02:00

998 lines
24 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, 2015 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 "twa/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;
static const acc_cond* debug_ref_acc = 0;
static const acc_cond* debug_cand_acc = 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;
acc_cond::mark_t acc;
unsigned dst;
transition_acc(int src, bdd cond, acc_cond::mark_t 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 < other.acc;
}
bool operator==(const transition_acc& other) const
{
return (this->src == other.src
&& this->dst == other.dst
&& this->cond.id() == other.cond.id()
&& this->acc == other.acc);
}
};
struct path
{
unsigned src_cand;
unsigned src_ref;
unsigned dst_cand;
unsigned dst_ref;
acc_cond::mark_t acc_cand;
acc_cond::mark_t 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(0U), acc_ref(0U)
{
}
path(unsigned src_cand, unsigned src_ref,
unsigned dst_cand, unsigned dst_ref,
acc_cond::mark_t acc_cand, acc_cond::mark_t 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 < other.acc_ref)
return true;
if (this->acc_ref > other.acc_ref)
return false;
if (this->acc_cand < other.acc_cand)
return true;
if (this->acc_cand > other.acc_cand)
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) << ','
<< debug_cand_acc->format(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 << ", "
<< debug_cand_acc->format(p.acc_cand) << ", "
<< debug_ref_acc->format(p.acc_ref) << '>';
return os;
}
struct dict
{
dict(const const_twa_ptr& a)
: aut(a)
{
}
const_twa_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<acc_cond::mark_t> cand_acc; // size cand_nacc
std::vector<acc_cond::mark_t> all_cand_acc;
std::vector<acc_cond::mark_t> all_ref_acc;
std::vector<bool> is_weak_scc;
acc_cond cacc;
~dict()
{
aut->get_dict()->unregister_all_my_variables(this);
}
};
unsigned declare_vars(const const_twa_graph_ptr& aut,
dict& d, bdd ap, bool state_based, scc_info& sm)
{
bdd_dict_ptr bd = aut->get_dict();
d.cand_acc.resize(d.cand_nacc);
d.cacc.add_sets(d.cand_nacc);
d.all_cand_acc.push_back(0U);
for (unsigned n = 0; n < d.cand_nacc; ++n)
{
auto c = d.cacc.mark(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.all_ref_acc.push_back(0U);
unsigned ref_nacc = aut->acc().num_sets();
for (unsigned n = 0; n < ref_nacc; ++n)
{
auto c = aut->acc().mark(n);
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);
}
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_twa_graph_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())
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();
debug_ref_acc = &ref->acc();
debug_cand_acc = &d.cacc;
dout << "ref_size: " << ref_size << '\n';
dout << "cand_size: " << d.cand_size << '\n';
#endif
auto& racc = ref->acc();
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";
{
unsigned init = ref->get_init_state_number();
dout << path(0, init) << '\n';
out << d.pathid[path(0, init)] << " 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;
}
}
}
}
// 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;
acc_cond::mark_t 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 &&
!racc.accepting
(curacc | d.all_ref_acc[fp])))
{
#if DEBUG
dout << "(11) " << p << ""
<< t << "δ → ¬(";
bool notfirst = false;
acc_cond::mark_t all_ =
d.all_cand_acc.back() -
d.all_cand_acc[f];
for (auto m: d.cacc.sets(all_))
{
transition_acc
ta(q2, l,
d.cacc.mark(m), q1);
if (notfirst)
out << "";
else
notfirst = true;
out << ta << "FC";
}
out << ")\n";
#endif // DEBUG
out << -pid << ' ' << -ti;
// 11
acc_cond::mark_t all_f =
d.all_cand_acc.back() -
d.all_cand_acc[f];
for (auto m: d.cacc.sets(all_f))
{
transition_acc
ta(q2, l,
d.cacc.mark(m), q1);
int tai = d.transaccid[ta];
assert(tai != 0);
out << ' ' << -tai;
}
out << " 0\n";
++nclauses;
}
else
{
#if DEBUG
dout << "(12) " << p << ""
<< t << "δ → (";
bool notfirst = false;
// 11
acc_cond::mark_t all_ =
d.cacc.comp(d.all_cand_acc[f]);
for (auto m: d.cacc.sets(all_))
{
transition_acc
ta(q2, l,
d.cacc.mark(m), q1);
if (notfirst)
out << "";
else
notfirst = true;
out << ta << "FC";
}
out << ")\n";
#endif // DEBUG
// 12
acc_cond::mark_t all_f =
d.cacc.comp(d.all_cand_acc[f]);
for (auto m: d.cacc.sets(all_f))
{
transition_acc
ta(q2, l,
d.cacc.mark(m), 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)
{
acc_cond::mark_t f2 =
p.acc_cand | d.all_cand_acc[f];
acc_cond::mark_t f2p = 0U;
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 << "δ ";
auto biga_ = d.all_cand_acc[f];
for (unsigned m = 0;
m < d.cand_nacc; ++m)
{
transition_acc
ta(q2, l,
d.cacc.mark(m), q3);
const char* not_ = "¬";
if (d.cacc.has(biga_, m))
not_ = "";
out << "" << not_
<< ta << "FC";
}
out << "" << p2 << '\n';
#endif
out << -pid << ' ' << -ti << ' ';
auto biga = d.all_cand_acc[f];
for (unsigned m = 0;
m < d.cand_nacc; ++m)
{
transition_acc
ta(q2, l,
d.cacc.mark(m), q3);
int tai = d.transaccid[ta];
if (d.cacc.has(biga, m))
tai = -tai;
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 twa_graph_ptr
sat_build(const satsolver::solution& solution, dict& satdict,
const_twa_graph_ptr aut, bool state_based)
{
auto autdict = aut->get_dict();
auto a = make_twa_graph(autdict);
a->copy_ap_of(aut);
a->set_generalized_buchi(satdict.cand_nacc);
if (state_based)
a->prop_state_based_acc();
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, acc_cond::mark_t> 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)
{
acc_cond::mark_t acc = 0U;
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);
auto& v = a->trans_data(last_aut_trans).acc;
v |= ta->second.acc;
}
else if (state_based)
{
auto& v = state_acc[ta->second.src];
v |= 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;
}
}
twa_graph_ptr
dtgba_sat_synthetize(const const_twa_graph_ptr& a,
unsigned target_acc_number,
int target_state_number, bool state_based)
{
if (!a->acc().is_generalized_buchi())
throw std::runtime_error
("dtgba_sat() can only work with generalized Büchi acceptance");
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");
twa_graph_ptr res = nullptr;
if (!solution.second.empty())
res = sat_build(solution.second, d, a, state_based);
// Always copy the environment variable into a static string,
// so that we (1) look it up once, but (2) won't crash if the
// environment is changed.
static std::string log = []()
{
auto s = getenv("SPOT_SATLOG");
return s ? s : "";
}();
if (!log.empty())
{
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 bool show = getenv("SPOT_SATSHOW");
if (show && res)
dotty_reachable(std::cout, res);
trace << "dtgba_sat_synthetize(...) = " << res << '\n';
return res;
}
twa_graph_ptr
dtgba_sat_minimize(const const_twa_graph_ptr& a,
unsigned target_acc_number,
bool state_based)
{
int n_states = stats_reachable(a).states;
twa_graph_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();
}
twa_graph_ptr
dtgba_sat_minimize_dichotomy(const const_twa_graph_ptr& a,
unsigned target_acc_number,
bool state_based)
{
int max_states = stats_reachable(a).states - 1;
int min_states = 1;
twa_graph_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;
}
}