introduce output_aborter, and use it in ltlcross

* spot/twaalgos/alternation.cc, spot/twaalgos/alternation.hh,
spot/twaalgos/complement.cc, spot/twaalgos/complement.hh,
spot/twaalgos/determinize.cc, spot/twaalgos/determinize.hh,
spot/twaalgos/minimize.cc, spot/twaalgos/minimize.hh,
spot/twaalgos/postproc.cc, spot/twaalgos/postproc.hh,
spot/twaalgos/powerset.cc, spot/twaalgos/powerset.hh,
spot/twaalgos/product.cc, spot/twaalgos/product.hh: Use an
output_aborter argument to abort if the output is too large.
* bin/ltlcross.cc: Use complement() with an output_aborter
so that ltlcross will not attempt to build complement larger
than 500 states or 5000 edges.  Add --determinize-max-states
and --determinize-max-edges options.
* tests/core/ltlcross3.test, tests/core/ltlcrossce2.test,
tests/core/sccsimpl.test, tests/core/wdba2.test,
tests/python/stutter-inv.ipynb: Adjust test cases.
* NEWS: Document this.
* bin/spot-x.cc: Add documentation for postprocessor's
det-max-states and det-max-edges arguments.
* doc/org/ltlcross.org: Update description.
This commit is contained in:
Alexandre Duret-Lutz 2019-05-27 23:08:13 +02:00
parent 5c3a33f720
commit a85045091b
23 changed files with 568 additions and 287 deletions

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2016-2018 Laboratoire de Recherche et Développement
// Copyright (C) 2016-2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -349,7 +349,7 @@ namespace spot
}
twa_graph_ptr run(bool named_states)
twa_graph_ptr run(bool named_states, const output_aborter* aborter)
{
// First, we classify each SCC into three possible classes:
//
@ -442,6 +442,9 @@ namespace spot
state_set v;
while (!todo.empty())
{
if (aborter && aborter->too_large(res))
return nullptr;
unsigned s = todo.top();
todo.pop();
@ -505,14 +508,15 @@ namespace spot
twa_graph_ptr remove_alternation(const const_twa_graph_ptr& aut,
bool named_states)
bool named_states,
const output_aborter* aborter)
{
if (aut->is_existential())
// Nothing to do, why was this function called at all?
return std::const_pointer_cast<twa_graph>(aut);
alternation_remover ar(aut);
return ar.run(named_states);
return ar.run(named_states, aborter);
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2016, 2018 Laboratoire de Recherche et Développement
// Copyright (C) 2016, 2018, 2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -20,6 +20,7 @@
#pragma once
#include <spot/twa/twagraph.hh>
#include <spot/twaalgos/powerset.hh>
#include <utility>
namespace spot
@ -98,10 +99,14 @@ namespace spot
/// acceptance is only used in presence of size-1 rejecting-SCCs.)
///
/// \param named_states name each state for easier debugging
///
/// \param aborter Return nullptr if the built automaton would
/// be larger than the size specified by the \a aborter.
/// @}
SPOT_API
twa_graph_ptr remove_alternation(const const_twa_graph_ptr& aut,
bool named_states = false);
bool named_states = false,
const output_aborter* aborter = nullptr);
// Remove universal edges on the fly.

View file

@ -511,17 +511,31 @@ namespace spot
}
twa_graph_ptr
complement(const const_twa_graph_ptr& aut)
complement(const const_twa_graph_ptr& aut, const output_aborter* aborter)
{
if (!aut->is_existential() || is_universal(aut))
return dualize(aut);
if (is_very_weak_automaton(aut))
return remove_alternation(dualize(aut));
return remove_alternation(dualize(aut), aborter);
// Determinize
spot::postprocessor p;
spot::option_map m;
if (aborter)
{
m.set("det-max-states", aborter->max_states());
m.set("det-max-edges", aborter->max_edges());
}
if (aut->num_states() > 32)
{
m.set("ba-simul", 0);
m.set("simul", 0);
}
spot::postprocessor p(&m);
p.set_type(spot::postprocessor::Generic);
p.set_pref(spot::postprocessor::Deterministic);
p.set_level(spot::postprocessor::Low);
return dualize(p.run(std::const_pointer_cast<twa_graph>(aut)));
auto det = p.run(std::const_pointer_cast<twa_graph>(aut));
if (!det)
return nullptr;
return dualize(det);
}
}

View file

@ -20,6 +20,7 @@
#pragma once
#include <spot/twa/twagraph.hh>
#include <spot/twaalgos/powerset.hh>
namespace spot
{
@ -70,8 +71,12 @@ namespace spot
/// - any other type of input is determized before
/// complementation.
///
/// If an output_aborter is supplied, it is used to
/// abort the construction of larger automata.
///
/// complement_semidet() is not yet used.
SPOT_API twa_graph_ptr
complement(const const_twa_graph_ptr& aut);
complement(const const_twa_graph_ptr& aut,
const output_aborter* aborter = nullptr);
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015-2018 Laboratoire de Recherche et
// Copyright (C) 2015-2019 Laboratoire de Recherche et
// Développement de l'Epita.
//
// This file is part of Spot, a model checking library.
@ -804,7 +804,8 @@ namespace spot
twa_graph_ptr
tgba_determinize(const const_twa_graph_ptr& a,
bool pretty_print, bool use_scc,
bool use_simulation, bool use_stutter)
bool use_simulation, bool use_stutter,
const output_aborter* aborter)
{
if (!a->is_existential())
throw std::runtime_error
@ -938,6 +939,8 @@ namespace spot
// The main loop
while (!todo.empty())
{
if (aborter && aborter->too_large(res))
return nullptr;
const safra_state& curr = todo.front().get().first;
unsigned src_num = todo.front().get().second;
todo.pop_front();

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015, 2016 Laboratoire de Recherche et Développement
// de l'Epita.
// Copyright (C) 2015, 2016, 2019 Laboratoire de Recherche et
// Développement de l'Epita.
//
// This file is part of Spot, a model checking library.
//
@ -19,6 +19,7 @@
#pragma once
#include <spot/twaalgos/powerset.hh>
#include <spot/twa/twagraph.hh>
namespace spot
@ -72,10 +73,15 @@ namespace spot
/// might be worth to call
/// spot::check_stutter_invariance() first if
/// possible.)
///
/// \param aborter abort the construction if the constructed
/// automaton would be too large. Return nullptr
/// in this case.
SPOT_API twa_graph_ptr
tgba_determinize(const const_twa_graph_ptr& aut,
bool pretty_print = false,
bool use_scc = true,
bool use_simulation = true,
bool use_stutter = true);
bool use_stutter = true,
const output_aborter* aborter = nullptr);
}

View file

@ -36,7 +36,6 @@
#include <spot/misc/hash.hh>
#include <spot/misc/bddlt.hh>
#include <spot/twaalgos/product.hh>
#include <spot/twaalgos/powerset.hh>
#include <spot/twaalgos/gtec/gtec.hh>
#include <spot/twaalgos/strength.hh>
#include <spot/twaalgos/sccfilter.hh>
@ -374,7 +373,8 @@ namespace spot
return res;
}
twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a)
twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a,
const output_aborter* aborter)
{
if (!a->is_existential())
throw std::runtime_error
@ -389,9 +389,15 @@ namespace spot
power_map pm;
bool input_is_det = is_deterministic(a);
if (input_is_det)
det_a = std::const_pointer_cast<twa_graph>(a);
{
det_a = std::const_pointer_cast<twa_graph>(a);
}
else
det_a = tgba_powerset(a, pm);
{
det_a = tgba_powerset(a, pm, aborter);
if (!det_a)
return nullptr;
}
// For each SCC of the deterministic automaton, determine if it
// is accepting or not.
@ -420,8 +426,10 @@ namespace spot
}
else
{
twa_graph_ptr prod = spot::product(a, det_a);
//print_dot(std::cerr, prod, "s");
twa_graph_ptr prod = spot::product(a, det_a, aborter);
if (!prod)
return nullptr;
const product_states* pmap =
prod->get_named_prop<product_states>("product-states");
assert(pmap);
@ -564,21 +572,38 @@ namespace spot
minimize_obligation(const const_twa_graph_ptr& aut_f,
formula f,
const_twa_graph_ptr aut_neg_f,
bool reject_bigger)
bool reject_bigger,
const output_aborter* aborter)
{
if (!aut_f->is_existential())
throw std::runtime_error
("minimize_obligation() does not support alternation");
// FIXME: We should build scc_info once, pass it to minimize_wdba
// and reuse it for is_terminal_automaton(),
// is_weak_automaton(), and is_very_weak_automaton().
// FIXME: we should postpone the call to minimize_wdba() until
// we know for sure that we can verify (or that we do not need
// to verify) its output, rather than computing in cases where
// we may discard it.
auto min_aut_f = minimize_wdba(aut_f);
bool minimization_will_be_correct = false;
// WDBA-minimization necessarily work for obligations
if ((f && f.is_syntactic_obligation())
// Weak deterministic automata are obligations
|| (aut_f->prop_weak() && is_deterministic(aut_f))
// Guarantee automata are obligations as well.
|| is_terminal_automaton(aut_f))
{
minimization_will_be_correct = true;
}
else if (!aut_neg_f)
{
// The minimization might not be correct and will need to
// be checked. Are we able to build aut_neg_f?
if (!(is_deterministic(aut_f) || f || is_very_weak_automaton(aut_f)))
return nullptr;
}
// FIXME: We should build scc_info once, and reuse it between
// minimize_wdba is_terminal_automaton(), is_weak_automaton(),
// and is_very_weak_automaton().
auto min_aut_f = minimize_wdba(aut_f, aborter);
if (!min_aut_f)
return std::const_pointer_cast<twa_graph>(aut_f);
if (reject_bigger)
{
// Abort if min_aut_f has more states than aut_f.
@ -587,29 +612,18 @@ namespace spot
return std::const_pointer_cast<twa_graph>(aut_f);
}
// if f is a syntactic obligation formula, the WDBA minimization
// must be correct.
if (f && f.is_syntactic_obligation())
if (minimization_will_be_correct)
return min_aut_f;
// If the input automaton was already weak and deterministic, the
// output is necessary correct.
if (aut_f->prop_weak() && is_deterministic(aut_f))
return min_aut_f;
// If aut_f is a guarantee automaton, the WDBA minimization must be
// correct.
if (is_terminal_automaton(aut_f))
return min_aut_f;
// Build negation automaton if not supplied.
// The minimization might not be correct and will need to
// be checked. Build negation automaton if not supplied.
if (!aut_neg_f)
{
if (is_deterministic(aut_f))
{
// If the automaton is deterministic, complementing is
// easy.
aut_neg_f = remove_fin(dualize(aut_f));
aut_neg_f = dualize(aut_f);
}
else if (f)
{
@ -631,15 +645,10 @@ namespace spot
return nullptr;
}
}
// If the negation is a guarantee automaton, then the
// minimization is correct.
if (is_terminal_automaton(aut_neg_f))
return min_aut_f;
// Make sure the minimized WDBA does not accept more words than
// the input.
if (product(min_aut_f, aut_neg_f)->is_empty())
auto prod = product(min_aut_f, aut_neg_f, aborter);
if (prod && prod->is_empty())
{
assert((bool)min_aut_f->prop_weak());
return min_aut_f;

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2019
// Laboratoire de Recherche et Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -20,6 +20,7 @@
#pragma once
#include <spot/twa/twagraph.hh>
#include <spot/twaalgos/powerset.hh>
#include <spot/tl/formula.hh>
namespace spot
@ -92,7 +93,12 @@ namespace spot
month = oct
}
\endverbatim */
SPOT_API twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a);
///
/// If an \a output_aborter is given, the determinization is aborted
/// whenever it would produce an automaton that is too large. In
/// that case, a nullptr is returned.
SPOT_API twa_graph_ptr minimize_wdba(const const_twa_graph_ptr& a,
const output_aborter* aborter = nullptr);
/// \brief Minimize an automaton if it represents an obligation property.
///
@ -149,10 +155,15 @@ namespace spot
/// determinization step during minimize_wdba().) Note that
/// checking the size of the minimized WDBA occurs before ensuring
/// that the minimized WDBA is correct.
///
/// If an \a output_aborter is given, the determinization is aborted
/// whenever it would produce an automaton that is too large. In
/// this case, aut_f is returned unchanged.
SPOT_API twa_graph_ptr
minimize_obligation(const const_twa_graph_ptr& aut_f,
formula f = nullptr,
const_twa_graph_ptr aut_neg_f = nullptr,
bool reject_bigger = false);
bool reject_bigger = false,
const output_aborter* aborter = nullptr);
/// @}
}

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012-2018 Laboratoire de Recherche et Développement
// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -71,6 +71,8 @@ namespace spot
det_scc_ = opt->get("det-scc", 1);
det_simul_ = opt->get("det-simul", 1);
det_stutter_ = opt->get("det-stutter", 1);
det_max_states_ = opt->get("det-max-states", -1);
det_max_edges_ = opt->get("det-max-edges", -1);
simul_ = opt->get("simul", -1);
scc_filter_ = opt->get("scc-filter", -1);
ba_simul_ = opt->get("ba-simul", -1);
@ -337,12 +339,19 @@ namespace spot
twa_graph_ptr dba = nullptr;
twa_graph_ptr sim = nullptr;
output_aborter aborter_
(det_max_states_ >= 0 ? static_cast<unsigned>(det_max_states_) : -1U,
det_max_edges_ >= 0 ? static_cast<unsigned>(det_max_edges_) : -1U);
output_aborter* aborter =
(det_max_states_ >= 0 || det_max_edges_ >= 0) ? &aborter_ : nullptr;
// (Small,Low) is the only configuration where we do not run
// WDBA-minimization.
if ((PREF_ != Small || level_ != Low) && wdba_minimize_)
{
// FIXME: This should be level_ <= Medium I believe.
bool reject_bigger = (PREF_ == Small) && (level_ == Medium);
dba = minimize_obligation(a, f, nullptr, reject_bigger);
dba = minimize_obligation(a, f, nullptr, reject_bigger, aborter);
if (dba
&& dba->prop_inherently_weak().is_true()
&& dba->prop_universal().is_true())
@ -459,11 +468,17 @@ namespace spot
if ((PREF_ == Deterministic && (type_ == Generic || want_parity)) && !dba)
{
dba = tgba_determinize(to_generalized_buchi(sim),
false, det_scc_, det_simul_, det_stutter_);
dba = simplify_acc(dba);
if (level_ != Low)
dba = simulation(dba);
sim = nullptr;
false, det_scc_, det_simul_, det_stutter_,
aborter);
// Setting det-max-states or det-max-edges may cause tgba_determinize
// to fail.
if (dba)
{
dba = simplify_acc(dba);
if (level_ != Low)
dba = simulation(dba);
sim = nullptr;
}
}
// Now dba contains either the result of WDBA-minimization (in

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012-2018 Laboratoire de Recherche et Développement
// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -240,6 +240,8 @@ namespace spot
bool det_scc_ = true;
bool det_simul_ = true;
bool det_stutter_ = true;
int det_max_states_ = -1;
int det_max_edges_ = -1;
int simul_ = -1;
int scc_filter_ = -1;
int ba_simul_ = -1;

View file

@ -70,8 +70,19 @@ namespace spot
};
}
std::ostream& output_aborter::print_reason(std::ostream& os) const
{
os << "more than ";
if (reason_is_states_)
os << max_states_ << " states required";
else
os << max_edges_ << " edges required";
return os;
}
twa_graph_ptr
tgba_powerset(const const_twa_graph_ptr& aut, power_map& pm, bool merge)
tgba_powerset(const const_twa_graph_ptr& aut, power_map& pm, bool merge,
const output_aborter* aborter)
{
unsigned ns = aut->num_states();
unsigned nap = aut->ap().size();
@ -245,6 +256,12 @@ namespace spot
pm.map_.emplace_back(std::move(ps));
}
res->new_edge(src_num, dst_num, num2bdd[c]);
if (aborter && aborter->too_large(res))
{
for (auto v: toclean)
delete v;
return nullptr;
}
}
}
@ -256,10 +273,11 @@ namespace spot
}
twa_graph_ptr
tgba_powerset(const const_twa_graph_ptr& aut)
tgba_powerset(const const_twa_graph_ptr& aut,
const output_aborter* aborter)
{
power_map pm;
return tgba_powerset(aut, pm);
return tgba_powerset(aut, pm, true, aborter);
}
@ -428,10 +446,13 @@ namespace spot
// Do not merge edges in the deterministic automaton. If we
// add two self-loops labeled by "a" and "!a", we do not want
// these to be merged as "1" before the acceptance has been fixed.
auto det = tgba_powerset(aut, pm, false);
if ((threshold_states > 0)
&& (pm.map_.size() > aut->num_states() * threshold_states))
unsigned max_states = aut->num_states() * threshold_states;
if (max_states == 0)
max_states = ~0U;
output_aborter aborter(max_states);
auto det = tgba_powerset(aut, pm, false, &aborter);
if (!det)
return nullptr;
if (fix_dba_acceptance(det, aut, pm, threshold_cycles))
return nullptr;

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2014, 2015 Laboratoire de Recherche et
// Copyright (C) 2011, 2013-2015, 2019 Laboratoire de Recherche et
// Développement de l'Epita.
// Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
@ -24,6 +24,7 @@
#include <set>
#include <vector>
#include <iosfwd>
#include <spot/twa/twagraph.hh>
namespace spot
@ -41,6 +42,43 @@ namespace spot
}
};
/// \brief Helper object to specify when an algorithm
/// should abort its construction.
class SPOT_API output_aborter
{
unsigned max_states_;
unsigned max_edges_;
mutable bool reason_is_states_;
public:
output_aborter(unsigned max_states,
unsigned max_edges = ~0U)
: max_states_(max_states), max_edges_(max_edges)
{
}
unsigned max_states() const
{
return max_states_;
}
unsigned max_edges() const
{
return max_edges_;
}
bool too_large(const const_twa_graph_ptr& aut) const
{
bool too_many_states = aut->num_states() > max_states_;
if (!too_many_states && (aut->num_edges() <= max_edges_))
return false;
// Only update the reason if we return true;
reason_is_states_ = too_many_states;
return true;
}
std::ostream& print_reason(std::ostream&) const;
};
/// \ingroup twa_misc
/// \brief Build a deterministic automaton, ignoring acceptance conditions.
@ -53,12 +91,17 @@ namespace spot
/// associated to each state of the deterministic automaton.
/// The \a merge argument can be set to false to prevent merging of
/// transitions.
///
/// If ab \a aborter is given, abort the construction whenever it
/// would build an automaton that is too large, and return nullptr.
//@{
SPOT_API twa_graph_ptr
tgba_powerset(const const_twa_graph_ptr& aut,
power_map& pm, bool merge = true);
power_map& pm, bool merge = true,
const output_aborter* aborter = nullptr);
SPOT_API twa_graph_ptr
tgba_powerset(const const_twa_graph_ptr& aut);
tgba_powerset(const const_twa_graph_ptr& aut,
const output_aborter* aborter = nullptr);
//@}

View file

@ -48,7 +48,8 @@ namespace spot
const const_twa_graph_ptr& right,
unsigned left_state,
unsigned right_state,
twa_graph_ptr res, T merge_acc)
twa_graph_ptr res, T merge_acc,
const output_aborter* aborter)
{
std::unordered_map<product_state, unsigned, product_state_hash> s2n;
std::deque<std::pair<product_state, unsigned>> todo;
@ -78,6 +79,11 @@ namespace spot
return;
while (!todo.empty())
{
if (aborter && aborter->too_large(res))
{
res = nullptr;
return;
}
auto top = todo.front();
todo.pop_front();
for (auto& l: left->out(top.first.first))
@ -99,7 +105,8 @@ namespace spot
const const_twa_graph_ptr& right,
unsigned left_state,
unsigned right_state,
bool and_acc)
bool and_acc,
const output_aborter* aborter)
{
if (SPOT_UNLIKELY(!(left->is_existential() && right->is_existential())))
throw std::runtime_error
@ -148,7 +155,7 @@ namespace spot
return accmark;
else
return rejmark;
});
}, aborter);
else
product_main(left, right, left_state, right_state, res,
[&] (acc_cond::mark_t ml, acc_cond::mark_t mr)
@ -157,7 +164,7 @@ namespace spot
return accmark;
else
return rejmark;
});
}, aborter);
}
else if (!rightweak)
{
@ -181,7 +188,7 @@ namespace spot
return mr;
else
return rejmark;
});
}, aborter);
}
else
{
@ -203,7 +210,7 @@ namespace spot
return mr;
else
return accmark;
});
}, aborter);
}
}
@ -230,7 +237,7 @@ namespace spot
return ml;
else
return rejmark;
});
}, aborter);
}
else
{
@ -252,7 +259,7 @@ namespace spot
return ml;
else
return accmark;
});
}, aborter);
}
}
@ -271,10 +278,13 @@ namespace spot
[&] (acc_cond::mark_t ml, acc_cond::mark_t mr)
{
return ml | (mr << left_num);
});
}, aborter);
}
if (!res) // aborted
return nullptr;
// The product of two non-deterministic automata could be
// deterministic. Likewise for non-complete automata.
if (left->prop_universal() && right->prop_universal())
@ -297,17 +307,19 @@ namespace spot
twa_graph_ptr product(const const_twa_graph_ptr& left,
const const_twa_graph_ptr& right,
unsigned left_state,
unsigned right_state)
unsigned right_state,
const output_aborter* aborter)
{
return product_aux(left, right, left_state, right_state, true);
return product_aux(left, right, left_state, right_state, true, aborter);
}
twa_graph_ptr product(const const_twa_graph_ptr& left,
const const_twa_graph_ptr& right)
const const_twa_graph_ptr& right,
const output_aborter* aborter)
{
return product(left, right,
left->get_init_state_number(),
right->get_init_state_number());
right->get_init_state_number(), aborter);
}
twa_graph_ptr product_or(const const_twa_graph_ptr& left,
@ -316,7 +328,7 @@ namespace spot
unsigned right_state)
{
return product_aux(complete(left), complete(right),
left_state, right_state, false);
left_state, right_state, false, nullptr);
}
twa_graph_ptr product_or(const const_twa_graph_ptr& left,

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 2015, 2018 Laboratoire de Recherche et
// Copyright (C) 2014, 2015, 2018, 2019 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
@ -21,6 +21,7 @@
#include <spot/misc/common.hh>
#include <spot/twa/fwd.hh>
#include <spot/twaalgos/powerset.hh>
#include <vector>
#include <utility>
@ -45,9 +46,13 @@ namespace spot
/// "product-states" with type spot::product_states. This stores
/// the pair of original state numbers associated to each state of
/// the product.
///
/// If an \a aborter is given, the function will return nullptr
/// whenever the resulting product would be too large.
SPOT_API
twa_graph_ptr product(const const_twa_graph_ptr& left,
const const_twa_graph_ptr& right);
const const_twa_graph_ptr& right,
const output_aborter* aborter = nullptr);
/// \ingroup twa_algorithms
/// \brief Intersect two automata using a synchronous product
@ -68,11 +73,15 @@ namespace spot
/// "product-states" with type spot::product_states. This stores
/// the pair of original state numbers associated to each state of
/// the product.
///
/// If an \a aborter is given, the function will return nullptr
/// whenever the resulting product would be too large.
SPOT_API
twa_graph_ptr product(const const_twa_graph_ptr& left,
const const_twa_graph_ptr& right,
unsigned left_state,
unsigned right_state);
unsigned right_state,
const output_aborter* aborter = nullptr);
/// \ingroup twa_algorithms
/// \brief Sum two automata using a synchronous product