This helps working around missing C functions like strcasecmp that do not exist everywhere (e.g. on Cygwin), and for which lib/ supplies a replacement. Unfortunately we do not have such build in our current continuous integration suite, so we cannot easily detect files where such config.h inclusion would be useful. Therefore this patch simply makes it mandatory to include config.h in *.cc files. Including this in public *.hh file is currently forbidden. * spot/gen/automata.cc, spot/gen/formulas.cc, spot/kripke/fairkripke.cc, spot/kripke/kripke.cc, spot/ltsmin/ltsmin.cc, spot/misc/game.cc, spot/parseaut/fmterror.cc, spot/parsetl/fmterror.cc, spot/parsetl/parsetl.yy, spot/priv/bddalloc.cc, spot/priv/freelist.cc, spot/priv/satcommon.cc, spot/priv/trim.cc, spot/priv/weight.cc, spot/ta/ta.cc, spot/ta/taexplicit.cc, spot/ta/taproduct.cc, spot/ta/tgtaexplicit.cc, spot/ta/tgtaproduct.cc, spot/taalgos/dot.cc, spot/taalgos/emptinessta.cc, spot/taalgos/minimize.cc, spot/taalgos/reachiter.cc, spot/taalgos/statessetbuilder.cc, spot/taalgos/stats.cc, spot/taalgos/tgba2ta.cc, spot/tl/apcollect.cc, spot/tl/contain.cc, spot/tl/declenv.cc, spot/tl/defaultenv.cc, spot/tl/dot.cc, spot/tl/exclusive.cc, spot/tl/hierarchy.cc, spot/tl/length.cc, spot/tl/ltlf.cc, spot/tl/mark.cc, spot/tl/mutation.cc, spot/tl/nenoform.cc, spot/tl/print.cc, spot/tl/randomltl.cc, spot/tl/relabel.cc, spot/tl/remove_x.cc, spot/tl/simplify.cc, spot/tl/snf.cc, spot/tl/unabbrev.cc, spot/twa/acc.cc, spot/twa/bdddict.cc, spot/twa/bddprint.cc, spot/twa/formula2bdd.cc, spot/twa/taatgba.cc, spot/twa/twa.cc, spot/twa/twagraph.cc, spot/twa/twaproduct.cc, spot/twaalgos/aiger.cc, spot/twaalgos/alternation.cc, spot/twaalgos/are_isomorphic.cc, spot/twaalgos/bfssteps.cc, spot/twaalgos/canonicalize.cc, spot/twaalgos/cleanacc.cc, spot/twaalgos/cobuchi.cc, spot/twaalgos/complement.cc, spot/twaalgos/complete.cc, spot/twaalgos/compsusp.cc, spot/twaalgos/couvreurnew.cc, spot/twaalgos/cycles.cc, spot/twaalgos/degen.cc, spot/twaalgos/determinize.cc, spot/twaalgos/dot.cc, spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc, spot/twaalgos/dualize.cc, spot/twaalgos/emptiness.cc, spot/twaalgos/gtec/ce.cc, spot/twaalgos/gtec/gtec.cc, spot/twaalgos/gtec/sccstack.cc, spot/twaalgos/gtec/status.cc, spot/twaalgos/gv04.cc, spot/twaalgos/hoa.cc, spot/twaalgos/iscolored.cc, spot/twaalgos/isdet.cc, spot/twaalgos/isunamb.cc, spot/twaalgos/isweakscc.cc, spot/twaalgos/langmap.cc, spot/twaalgos/lbtt.cc, spot/twaalgos/ltl2taa.cc, spot/twaalgos/ltl2tgba_fm.cc, spot/twaalgos/magic.cc, spot/twaalgos/mask.cc, spot/twaalgos/minimize.cc, spot/twaalgos/neverclaim.cc, spot/twaalgos/parity.cc, spot/twaalgos/postproc.cc, spot/twaalgos/powerset.cc, spot/twaalgos/product.cc, spot/twaalgos/rabin2parity.cc, spot/twaalgos/randomgraph.cc, spot/twaalgos/randomize.cc, spot/twaalgos/reachiter.cc, spot/twaalgos/relabel.cc, spot/twaalgos/remfin.cc, spot/twaalgos/remprop.cc, spot/twaalgos/sbacc.cc, spot/twaalgos/sccfilter.cc, spot/twaalgos/sccinfo.cc, spot/twaalgos/se05.cc, spot/twaalgos/sepsets.cc, spot/twaalgos/simulation.cc, spot/twaalgos/split.cc, spot/twaalgos/stats.cc, spot/twaalgos/strength.cc, spot/twaalgos/stripacc.cc, spot/twaalgos/stutter.cc, spot/twaalgos/sum.cc, spot/twaalgos/tau03.cc, spot/twaalgos/tau03opt.cc, spot/twaalgos/totgba.cc, spot/twaalgos/toweak.cc, spot/twaalgos/translate.cc, spot/twaalgos/word.cc, tests/core/acc.cc, tests/core/bitvect.cc, tests/core/checkpsl.cc, tests/core/checkta.cc, tests/core/consterm.cc, tests/core/emptchk.cc, tests/core/equalsf.cc, tests/core/graph.cc, tests/core/ikwiad.cc, tests/core/intvcmp2.cc, tests/core/intvcomp.cc, tests/core/kind.cc, tests/core/kripkecat.cc, tests/core/length.cc, tests/core/ltlrel.cc, tests/core/ngraph.cc, tests/core/parity.cc, tests/core/randtgba.cc, tests/core/readltl.cc, tests/core/reduc.cc, tests/core/safra.cc, tests/core/sccif.cc, tests/core/syntimpl.cc, tests/core/taatgba.cc, tests/core/tostring.cc, tests/core/trival.cc, tests/core/twagraph.cc, tests/ltsmin/modelcheck.cc, spot/parseaut/scanaut.ll, spot/parsetl/scantl.ll: Include config.h. * spot/gen/Makefile.am, spot/graph/Makefile.am, spot/kripke/Makefile.am, spot/ltsmin/Makefile.am, spot/parseaut/Makefile.am, spot/parsetl/Makefile.am, spot/priv/Makefile.am, spot/ta/Makefile.am, spot/taalgos/Makefile.am, spot/tl/Makefile.am, spot/twa/Makefile.am, spot/twaalgos/Makefile.am, spot/twaalgos/gtec/Makefile.am, tests/Makefile.am: Add the -I lib/ flags. * tests/sanity/includes.test: Catch missing config.h in *.cc, and diagnose config.h in *.hh. * tests/sanity/style.test: Better diagnostics.
421 lines
13 KiB
C++
421 lines
13 KiB
C++
// -*- coding: utf-8 -*-
|
|
// Copyright (C) 2017-2018 Laboratoire de Recherche et Développement
|
|
// 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 "config.h"
|
|
#include <spot/twaalgos/aiger.hh>
|
|
|
|
#include <cmath>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <spot/twa/twagraph.hh>
|
|
#include <spot/misc/bddlt.hh>
|
|
#include <spot/misc/minato.hh>
|
|
|
|
namespace spot
|
|
{
|
|
namespace
|
|
{
|
|
static std::vector<std::string>
|
|
name_vector(unsigned n, const std::string& prefix)
|
|
{
|
|
std::vector<std::string> res(n);
|
|
for (unsigned i = 0; i != n; ++i)
|
|
res[i] = prefix + std::to_string(i);
|
|
return res;
|
|
}
|
|
|
|
// A class to represent an AIGER circuit
|
|
class aig
|
|
{
|
|
private:
|
|
unsigned num_inputs_;
|
|
unsigned max_var_;
|
|
std::map<unsigned, std::pair<unsigned, unsigned>> and_gates_;
|
|
std::vector<unsigned> latches_;
|
|
std::vector<unsigned> outputs_;
|
|
std::vector<std::string> input_names_;
|
|
std::vector<std::string> output_names_;
|
|
// Cache the function computed by each variable as a bdd.
|
|
std::unordered_map<unsigned, bdd> var2bdd_;
|
|
std::unordered_map<bdd, unsigned, bdd_hash> bdd2var_;
|
|
|
|
public:
|
|
aig(const std::vector<std::string>& inputs,
|
|
const std::vector<std::string>& outputs,
|
|
unsigned num_latches)
|
|
: num_inputs_(inputs.size()),
|
|
max_var_((inputs.size() + num_latches)*2),
|
|
latches_(num_latches),
|
|
outputs_(outputs.size()),
|
|
input_names_(inputs),
|
|
output_names_(outputs)
|
|
{
|
|
bdd2var_[bddtrue] = 1;
|
|
var2bdd_[1] = bddtrue;
|
|
bdd2var_[bddfalse] = 0;
|
|
var2bdd_[0] = bddfalse;
|
|
}
|
|
|
|
aig(unsigned num_inputs, unsigned num_latches, unsigned num_outputs)
|
|
: aig(name_vector(num_inputs, "in"), name_vector(num_outputs, "out"),
|
|
num_latches)
|
|
{}
|
|
|
|
unsigned input_var(unsigned i, bdd b)
|
|
{
|
|
assert(i < num_inputs_);
|
|
unsigned v = (1 + i) * 2;
|
|
bdd2var_[b] = v;
|
|
var2bdd_[v] = b;
|
|
return v;
|
|
}
|
|
|
|
unsigned latch_var(unsigned i, bdd b)
|
|
{
|
|
assert(i < latches_.size());
|
|
unsigned v = (1 + num_inputs_ + i) * 2;
|
|
bdd2var_[b] = v;
|
|
var2bdd_[v] = b;
|
|
return v;
|
|
}
|
|
|
|
void set_output(unsigned i, unsigned v)
|
|
{
|
|
outputs_[i] = v;
|
|
}
|
|
|
|
void set_latch(unsigned i, unsigned v)
|
|
{
|
|
latches_[i] = v;
|
|
}
|
|
|
|
unsigned aig_true() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
unsigned aig_false() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
unsigned aig_not(unsigned v)
|
|
{
|
|
unsigned not_v = v + 1 - 2 * (v % 2);
|
|
assert(var2bdd_.count(v));
|
|
var2bdd_[not_v] = !(var2bdd_[v]);
|
|
bdd2var_[var2bdd_[not_v]] = not_v;
|
|
return not_v;
|
|
}
|
|
|
|
unsigned aig_and(unsigned v1, unsigned v2)
|
|
{
|
|
assert(var2bdd_.count(v1));
|
|
assert(var2bdd_.count(v2));
|
|
bdd b = var2bdd_[v1] & var2bdd_[v2];
|
|
auto it = bdd2var_.find(b);
|
|
if (it != bdd2var_.end())
|
|
return it->second;
|
|
max_var_ += 2;
|
|
and_gates_[max_var_] = {v1, v2};
|
|
bdd v1v2 = var2bdd_[v1] & var2bdd_[v2];
|
|
bdd2var_[v1v2] = max_var_;
|
|
var2bdd_[max_var_] = v1v2;
|
|
return max_var_;
|
|
}
|
|
|
|
unsigned aig_and(std::vector<unsigned> vs)
|
|
{
|
|
if (vs.empty())
|
|
return aig_true();
|
|
if (vs.size() == 1)
|
|
return vs[0];
|
|
if (vs.size() == 2)
|
|
return aig_and(vs[0], vs[1]);
|
|
unsigned m = vs.size() / 2;
|
|
std::vector<unsigned> left(vs.begin(), vs.begin() + m);
|
|
std::vector<unsigned> right(vs.begin() + m, vs.end());
|
|
return aig_and(aig_and(left), aig_and(right));
|
|
}
|
|
|
|
unsigned aig_or(unsigned v1, unsigned v2)
|
|
{
|
|
return aig_not(aig_and(aig_not(v1), aig_not(v2)));
|
|
}
|
|
|
|
unsigned aig_or(std::vector<unsigned> vs)
|
|
{
|
|
for (unsigned i = 0; i < vs.size(); ++i)
|
|
vs[i] = aig_not(vs[i]);
|
|
return aig_not(aig_and(vs));
|
|
}
|
|
|
|
unsigned aig_pos(unsigned v)
|
|
{
|
|
return v - v % 2;
|
|
}
|
|
|
|
void remove_unused()
|
|
{
|
|
std::unordered_set<unsigned> todo;
|
|
for (unsigned v : outputs_)
|
|
todo.insert(aig_pos(v));
|
|
std::unordered_set<unsigned> used;
|
|
while (!todo.empty())
|
|
{
|
|
used.insert(todo.begin(), todo.end());
|
|
std::unordered_set<unsigned> todo_next;
|
|
for (unsigned v : todo)
|
|
{
|
|
auto it_and = and_gates_.find(v);
|
|
if (it_and != and_gates_.end())
|
|
{
|
|
if (!used.count(aig_pos(it_and->second.first)))
|
|
todo_next.insert(aig_pos(it_and->second.first));
|
|
if (!used.count(aig_pos(it_and->second.second)))
|
|
todo_next.insert(aig_pos(it_and->second.second));
|
|
}
|
|
else if (v <= (num_inputs_ + latches_.size()) * 2
|
|
&& v > num_inputs_ * 2)
|
|
{
|
|
unsigned l = v / 2 - num_inputs_ - 1;
|
|
if (!used.count(aig_pos(latches_[l])))
|
|
todo_next.insert(aig_pos(latches_[l]));
|
|
}
|
|
}
|
|
todo = todo_next;
|
|
}
|
|
std::unordered_set<unsigned> unused;
|
|
for (auto& p : and_gates_)
|
|
if (!used.count(p.first))
|
|
unused.insert(p.first);
|
|
for (unsigned v : unused)
|
|
and_gates_.erase(v);
|
|
}
|
|
|
|
void
|
|
print(std::ostream& os) const
|
|
{
|
|
os << "aag " << max_var_ / 2
|
|
<< ' ' << num_inputs_
|
|
<< ' ' << latches_.size()
|
|
<< ' ' << outputs_.size()
|
|
<< ' ' << and_gates_.size() << '\n';
|
|
for (unsigned i = 0; i < num_inputs_; ++i)
|
|
os << (1 + i) * 2 << '\n';
|
|
for (unsigned i = 0; i < latches_.size(); ++i)
|
|
os << (1 + num_inputs_ + i) * 2 << ' ' << latches_[i] << '\n';
|
|
for (unsigned i = 0; i < outputs_.size(); ++i)
|
|
os << outputs_[i] << '\n';
|
|
for (auto& p : and_gates_)
|
|
os << p.first
|
|
<< ' ' << p.second.first
|
|
<< ' ' << p.second.second << '\n';
|
|
for (unsigned i = 0; i < num_inputs_; ++i)
|
|
os << 'i' << i << ' ' << input_names_[i] << '\n';
|
|
for (unsigned i = 0; i < outputs_.size(); ++i)
|
|
os << 'o' << i << ' ' << output_names_[i] << '\n';
|
|
}
|
|
|
|
};
|
|
|
|
static std::vector<bool>
|
|
state_to_vec(unsigned s, unsigned size)
|
|
{
|
|
std::vector<bool> v(size);
|
|
for (unsigned i = 0; i < size; ++i)
|
|
{
|
|
v[i] = s % 2;
|
|
s >>= 1;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static std::vector<bool>
|
|
output_to_vec(bdd b,
|
|
const std::unordered_map<unsigned, unsigned>& bddvar_to_outputnum)
|
|
{
|
|
std::vector<bool> v(bddvar_to_outputnum.size());
|
|
while (b != bddtrue && b != bddfalse)
|
|
{
|
|
unsigned i = bddvar_to_outputnum.at(bdd_var(b));
|
|
v[i] = (bdd_low(b) == bddfalse);
|
|
if (v[i])
|
|
b = bdd_high(b);
|
|
else
|
|
b = bdd_low(b);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static bdd
|
|
state_to_bdd(unsigned s, bdd all)
|
|
{
|
|
bdd b = bddtrue;
|
|
unsigned size = bdd_nodecount(all);
|
|
if (size)
|
|
{
|
|
unsigned st0 = bdd_var(all);
|
|
for (unsigned i = 0; i < size; ++i)
|
|
{
|
|
b &= s % 2 ? bdd_ithvar(st0 + i) : bdd_nithvar(st0 + i);
|
|
s >>= 1;
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
// Switch initial state and 0 in the AIGER encoding, so that the
|
|
// 0-initialized latches correspond to the initial state.
|
|
static unsigned
|
|
encode_init_0(unsigned src, unsigned init)
|
|
{
|
|
return src == init ? 0 : src == 0 ? init : src;
|
|
}
|
|
|
|
// Transforms an automaton into an AIGER circuit
|
|
static aig
|
|
aut_to_aiger(const const_twa_graph_ptr& aut, const bdd& all_outputs)
|
|
{
|
|
// The aiger circuit cannot encode the acceptance condition
|
|
// Test that the acceptance condition is true
|
|
if (!aut->acc().is_t())
|
|
throw std::runtime_error("Cannot turn automaton into aiger circuit");
|
|
|
|
// Encode state in log2(num_states) latches.
|
|
// TODO how hard is it to compute the binary log of a binary integer??
|
|
unsigned log2n = std::ceil(std::log2(aut->num_states()));
|
|
unsigned st0 = aut->get_dict()->register_anonymous_variables(log2n, aut);
|
|
bdd all_states = bddtrue;
|
|
for (unsigned i = 0; i < log2n; ++i)
|
|
all_states &= bdd_ithvar(st0 + i);
|
|
|
|
std::vector<std::string> input_names;
|
|
std::vector<std::string> output_names;
|
|
bdd all_inputs = bddtrue;
|
|
std::unordered_map<unsigned, unsigned> bddvar_to_inputnum;
|
|
std::unordered_map<unsigned, unsigned> bddvar_to_outputnum;
|
|
for (const auto& ap : aut->ap())
|
|
{
|
|
int bddvar = aut->get_dict()->has_registered_proposition(ap, aut);
|
|
assert(bddvar >= 0);
|
|
bdd b = bdd_ithvar(bddvar);
|
|
if (bdd_implies(b, all_outputs)) // ap is an output AP
|
|
{
|
|
bddvar_to_outputnum[bddvar] = output_names.size();
|
|
output_names.emplace_back(ap.ap_name());
|
|
}
|
|
else // ap is an input AP
|
|
{
|
|
bddvar_to_inputnum[bddvar] = input_names.size();
|
|
input_names.emplace_back(ap.ap_name());
|
|
all_inputs &= b;
|
|
}
|
|
}
|
|
|
|
unsigned num_outputs = bdd_nodecount(all_outputs);
|
|
unsigned num_latches = bdd_nodecount(all_states);
|
|
unsigned init = aut->get_init_state_number();
|
|
|
|
aig circuit(input_names, output_names, num_latches);
|
|
bdd b;
|
|
|
|
// Latches and outputs are expressed as a DNF in which each term
|
|
// represents a transition.
|
|
// latch[i] (resp. out[i]) represents the i-th latch (resp. output) DNF.
|
|
std::vector<std::vector<unsigned>> latch(num_latches);
|
|
std::vector<std::vector<unsigned>> out(num_outputs);
|
|
for (unsigned s = 0; s < aut->num_states(); ++s)
|
|
for (auto& e: aut->out(s))
|
|
{
|
|
minato_isop cond(e.cond);
|
|
while ((b = cond.next()) != bddfalse)
|
|
{
|
|
bdd input = bdd_existcomp(b, all_inputs);
|
|
bdd letter_out = bdd_existcomp(b, all_outputs);
|
|
auto out_vec = output_to_vec(letter_out, bddvar_to_outputnum);
|
|
unsigned dst = encode_init_0(e.dst, init);
|
|
auto next_state_vec = state_to_vec(dst, log2n);
|
|
unsigned src = encode_init_0(s, init);
|
|
bdd state_bdd = state_to_bdd(src, all_states);
|
|
std::vector<unsigned> prod;
|
|
while (input != bddfalse && input != bddtrue)
|
|
{
|
|
unsigned v =
|
|
circuit.input_var(bddvar_to_inputnum[bdd_var(input)],
|
|
bdd_ithvar(bdd_var(input)));
|
|
if (bdd_high(input) == bddfalse)
|
|
{
|
|
v = circuit.aig_not(v);
|
|
input = bdd_low(input);
|
|
}
|
|
else
|
|
input = bdd_high(input);
|
|
prod.push_back(v);
|
|
}
|
|
|
|
while (state_bdd != bddfalse && state_bdd != bddtrue)
|
|
{
|
|
unsigned v =
|
|
circuit.latch_var(bdd_var(state_bdd) - st0,
|
|
bdd_ithvar(bdd_var(state_bdd)));
|
|
if (bdd_high(state_bdd) == bddfalse)
|
|
{
|
|
v = circuit.aig_not(v);
|
|
state_bdd = bdd_low(state_bdd);
|
|
}
|
|
else
|
|
state_bdd = bdd_high(state_bdd);
|
|
prod.push_back(v);
|
|
}
|
|
unsigned t = circuit.aig_and(prod);
|
|
for (unsigned i = 0; i < next_state_vec.size(); ++i)
|
|
if (next_state_vec[i])
|
|
latch[i].push_back(t);
|
|
for (unsigned i = 0; i < num_outputs; ++i)
|
|
if (out_vec[i])
|
|
out[i].push_back(t);
|
|
}
|
|
}
|
|
for (unsigned i = 0; i < num_latches; ++i)
|
|
circuit.set_latch(i, circuit.aig_or(latch[i]));
|
|
for (unsigned i = 0; i < num_outputs; ++i)
|
|
circuit.set_output(i, circuit.aig_or(out[i]));
|
|
circuit.remove_unused();
|
|
return circuit;
|
|
}
|
|
}
|
|
|
|
std::ostream&
|
|
print_aiger(std::ostream& os, const const_twa_ptr& aut)
|
|
{
|
|
auto a = down_cast<const_twa_graph_ptr>(aut);
|
|
if (!a)
|
|
throw std::runtime_error("aiger output is only for twa_graph");
|
|
|
|
bdd* all_outputs = aut->get_named_prop<bdd>("synthesis-outputs");
|
|
|
|
aig circuit = aut_to_aiger(a, all_outputs ? *all_outputs : bdd(bddfalse));
|
|
circuit.print(os);
|
|
return os;
|
|
}
|
|
}
|