ltlsynt: detect APs with constant polarity
This implements the first point of issue #529. * spot/tl/apcollect.cc, spot/tl/apcollect.hh (collect_litterals): New function. * bin/ltlsynt.cc: Implement the --polarity option, use collect_litterals() to simplify the specification, finally patch the game, Mealy, or Aiger output. * spot/twaalgos/aiger.cc, spot/twaalgos/aiger.hh: Take a relabeling_map has argument to specify extra APs. * tests/core/ltlsynt.test, tests/core/ltlsynt2.test: Adjust test cases.
This commit is contained in:
parent
abca0f7fd9
commit
202ab92d1d
8 changed files with 378 additions and 76 deletions
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2014, 2015, 2018 Laboratoire de Recherche et
|
||||
// Copyright (C) 2012, 2014, 2015, 2018, 2023 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
|
|
@ -63,4 +63,72 @@ namespace spot
|
|||
res &= bdd_ithvar(a->register_ap(f));
|
||||
return res;
|
||||
}
|
||||
|
||||
atomic_prop_set collect_litterals(formula f)
|
||||
{
|
||||
atomic_prop_set res;
|
||||
|
||||
// polirity: 0 = negative, 1 = positive, 2 or more = both.
|
||||
auto rec = [&res](formula f, unsigned polarity, auto self)
|
||||
{
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::ff:
|
||||
case op::tt:
|
||||
case op::eword:
|
||||
return;
|
||||
case op::ap:
|
||||
if (polarity != 0)
|
||||
res.insert(f);
|
||||
if (polarity != 1)
|
||||
res.insert(formula::Not(f));
|
||||
return;
|
||||
case op::Not:
|
||||
case op::NegClosure:
|
||||
case op::NegClosureMarked:
|
||||
self(f[0], polarity ^ 1, self);
|
||||
return;
|
||||
case op::Xor:
|
||||
case op::Equiv:
|
||||
self(f[0], 2, self);
|
||||
self(f[1], 2, self);
|
||||
return;
|
||||
case op::Implies:
|
||||
case op::UConcat:
|
||||
self(f[0], polarity ^ 1, self);
|
||||
self(f[1], polarity, self);
|
||||
return;
|
||||
case op::U:
|
||||
case op::R:
|
||||
case op::W:
|
||||
case op::M:
|
||||
case op::EConcat:
|
||||
case op::EConcatMarked:
|
||||
self(f[0], polarity, self);
|
||||
self(f[1], polarity, self);
|
||||
return;
|
||||
case op::X:
|
||||
case op::F:
|
||||
case op::G:
|
||||
case op::Closure:
|
||||
case op::Or:
|
||||
case op::OrRat:
|
||||
case op::And:
|
||||
case op::AndRat:
|
||||
case op::AndNLM:
|
||||
case op::Concat:
|
||||
case op::Fusion:
|
||||
case op::Star:
|
||||
case op::FStar:
|
||||
case op::first_match:
|
||||
case op::strong_X:
|
||||
for (formula c: f)
|
||||
self(c, polarity, self);
|
||||
return;
|
||||
}
|
||||
};
|
||||
rec(f, 1, rec);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
|
||||
// Copyright (C) 2012, 2013, 2014, 2015, 2023 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
|
||||
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
|
||||
|
|
@ -59,5 +59,15 @@ namespace spot
|
|||
SPOT_API bdd
|
||||
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a);
|
||||
|
||||
|
||||
/// \brief Collect the litterals occuring in f
|
||||
///
|
||||
/// This function records each atomic proposition occurring in f
|
||||
/// along with the polarity of its occurrence. For instance if the
|
||||
/// formula is `G(a -> b) & X(!b & c)`, then this will output `{!a,
|
||||
/// b, !b, c}`.
|
||||
SPOT_API
|
||||
atomic_prop_set collect_litterals(formula f);
|
||||
|
||||
/// @}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1541,10 +1541,11 @@ namespace
|
|||
// outputs into an Aig
|
||||
static aig_ptr
|
||||
auts_to_aiger(const std::vector<std::pair<const_twa_graph_ptr, bdd>>&
|
||||
strat_vec,
|
||||
strat_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& unused_ins = {},
|
||||
const std::vector<std::string>& unused_outs = {})
|
||||
const std::vector<std::string>& unused_outs = {},
|
||||
const relabeling_map* rm = nullptr)
|
||||
{
|
||||
// The aiger circuit can currently noly encode separated mealy machines
|
||||
|
||||
|
|
@ -1619,6 +1620,23 @@ namespace
|
|||
unused_outs.cbegin(),
|
||||
unused_outs.cend());
|
||||
|
||||
if (rm)
|
||||
// If we have removed some APs from the original formula, they
|
||||
// might have dropped out of the output_names list (depending on
|
||||
// how we split the formula), but they should not have dropped
|
||||
// from the input_names list. So let's fix the output_names
|
||||
// lists by adding anything that's not an input and not already
|
||||
// there.
|
||||
for (auto [k, v]: *rm)
|
||||
{
|
||||
const std::string s = k.ap_name();
|
||||
if (std::find(input_names_all.begin(), input_names_all.end(), s)
|
||||
== input_names_all.end()
|
||||
&& std::find(output_names_all.begin(), output_names_all.end(), s)
|
||||
== output_names_all.end())
|
||||
output_names_all.push_back(s);
|
||||
}
|
||||
|
||||
// Decide on which outcond to use
|
||||
// The edges of the automaton all have the form in&out
|
||||
// due to the unsplit
|
||||
|
|
@ -1962,7 +1980,7 @@ namespace
|
|||
}
|
||||
//Use the best sol
|
||||
circuit.reapply_(sf, ss);
|
||||
trace << "Finished encoding, reasssigning\n"
|
||||
trace << "Finished encoding, reassigning\n"
|
||||
<< "Final gate count is " << circuit.num_gates() << '\n';
|
||||
// Reset them
|
||||
for (unsigned i = 0; i < n_outs; ++i)
|
||||
|
|
@ -1970,7 +1988,25 @@ namespace
|
|||
// Add the unused propositions
|
||||
const unsigned n_outs_all = output_names_all.size();
|
||||
for (unsigned i = n_outs; i < n_outs_all; ++i)
|
||||
circuit.set_output(i, circuit.aig_false());
|
||||
if (rm)
|
||||
{
|
||||
if (auto to = rm->find(formula::ap(output_names_all[i]));
|
||||
to != rm->end())
|
||||
{
|
||||
if (to->second.is_tt())
|
||||
{
|
||||
circuit.set_output(i, circuit.aig_true());
|
||||
continue;
|
||||
}
|
||||
else if (to->second.is_ff())
|
||||
{
|
||||
circuit.set_output(i, circuit.aig_false());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
circuit.set_output(i, circuit.aig_false());
|
||||
for (unsigned i = 0; i < n_latches; ++i)
|
||||
circuit.set_next_latch(i, bdd2var_min(latch[i], bddfalse));
|
||||
return circuit_ptr;
|
||||
|
|
@ -2002,8 +2038,9 @@ namespace spot
|
|||
|
||||
aig_ptr
|
||||
mealy_machine_to_aig(const twa_graph_ptr &m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs)
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs,
|
||||
const relabeling_map* rm)
|
||||
{
|
||||
if (!m)
|
||||
throw std::runtime_error("mealy_machine_to_aig(): "
|
||||
|
|
@ -2036,19 +2073,20 @@ namespace spot
|
|||
}
|
||||
// todo Some additional checks?
|
||||
return auts_to_aiger({{m, get_synthesis_outputs(m)}}, mode,
|
||||
unused_ins, unused_outs);
|
||||
unused_ins, unused_outs, rm);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
mealy_machine_to_aig(mealy_like& m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs)
|
||||
const std::vector<std::string>& outs,
|
||||
const relabeling_map* rm)
|
||||
{
|
||||
if (m.success != mealy_like::realizability_code::REALIZABLE_REGULAR)
|
||||
throw std::runtime_error("mealy_machine_to_aig(): "
|
||||
"Can only handle regular mealy machine, yet.");
|
||||
|
||||
return mealy_machine_to_aig(m.mealy_like, mode, ins, outs);
|
||||
return mealy_machine_to_aig(m.mealy_like, mode, ins, outs, rm);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
|
|
@ -2107,7 +2145,8 @@ namespace spot
|
|||
mealy_machines_to_aig(const std::vector<const_twa_graph_ptr>& m_vec,
|
||||
const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs)
|
||||
const std::vector<std::vector<std::string>>& outs,
|
||||
const relabeling_map* rm)
|
||||
{
|
||||
if (m_vec.empty())
|
||||
throw std::runtime_error("mealy_machines_to_aig(): No strategy given.");
|
||||
|
|
@ -2164,14 +2203,15 @@ namespace spot
|
|||
if (!used_aps.count(ai))
|
||||
unused_ins.push_back(ai);
|
||||
|
||||
return auts_to_aiger(new_vec, mode, unused_ins, unused_outs);
|
||||
return auts_to_aiger(new_vec, mode, unused_ins, unused_outs, rm);
|
||||
}
|
||||
|
||||
aig_ptr
|
||||
mealy_machines_to_aig(const std::vector<mealy_like>& strat_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs)
|
||||
const std::vector<std::vector<std::string>>& outs,
|
||||
const relabeling_map* rm)
|
||||
{
|
||||
// todo extend to TGBA and possibly others
|
||||
const unsigned ns = strat_vec.size();
|
||||
|
|
@ -2205,7 +2245,7 @@ namespace spot
|
|||
"success identifier.");
|
||||
}
|
||||
}
|
||||
return mealy_machines_to_aig(m_machines, mode, ins, outs_used);
|
||||
return mealy_machines_to_aig(m_machines, mode, ins, outs_used, rm);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <spot/twa/fwd.hh>
|
||||
#include <spot/twa/bdddict.hh>
|
||||
#include <spot/tl/formula.hh>
|
||||
#include <spot/tl/relabel.hh>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
|
@ -436,20 +437,25 @@ namespace spot
|
|||
/// If \a ins and \a outs are specified, the named-property
|
||||
/// synthesis-output is ignored and all properties in \a ins and \a
|
||||
/// outs are guaranteed to appear in the aiger circuit.
|
||||
///
|
||||
/// If \a rm is given and is not empty, it can be used to specify how
|
||||
/// unused output should be encoded by mapping them to some constant.
|
||||
///@{
|
||||
SPOT_API aig_ptr
|
||||
mealy_machine_to_aig(const const_twa_graph_ptr& m, const char* mode);
|
||||
SPOT_API aig_ptr
|
||||
mealy_machine_to_aig(const twa_graph_ptr& m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs);
|
||||
const std::vector<std::string>& outs,
|
||||
const relabeling_map* rm = nullptr);
|
||||
|
||||
SPOT_API aig_ptr
|
||||
mealy_machine_to_aig(const mealy_like& m, const char* mode);
|
||||
SPOT_API aig_ptr
|
||||
mealy_machine_to_aig(mealy_like& m, const char *mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::string>& outs);
|
||||
const std::vector<std::string>& outs,
|
||||
const relabeling_map* rm = nullptr);
|
||||
///@}
|
||||
|
||||
/// \ingroup synthesis
|
||||
|
|
@ -465,6 +471,9 @@ namespace spot
|
|||
/// during the call to ltl_to_game() are absent.
|
||||
/// If \a ins and \a outs are used, all properties they list are
|
||||
/// guaranteed to appear in the aiger circuit.
|
||||
///
|
||||
/// If \a rm is given and is not empty, it can be used to specify how
|
||||
/// unused output should be encoded by mapping them to some constant.
|
||||
/// @{
|
||||
SPOT_API aig_ptr
|
||||
mealy_machines_to_aig(const std::vector<const_twa_graph_ptr>& m_vec,
|
||||
|
|
@ -476,12 +485,14 @@ namespace spot
|
|||
mealy_machines_to_aig(const std::vector<const_twa_graph_ptr>& m_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs);
|
||||
const std::vector<std::vector<std::string>>& outs,
|
||||
const relabeling_map* rm = nullptr);
|
||||
SPOT_API aig_ptr
|
||||
mealy_machines_to_aig(const std::vector<mealy_like>& m_vec,
|
||||
const char* mode,
|
||||
const std::vector<std::string>& ins,
|
||||
const std::vector<std::vector<std::string>>& outs);
|
||||
const std::vector<std::vector<std::string>>& outs,
|
||||
const relabeling_map* rm = nullptr);
|
||||
/// @}
|
||||
|
||||
/// \ingroup twa_io
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue