1#ifndef LALA_CORE_TERNARIZE_HPP
2#define LALA_CORE_TERNARIZE_HPP
12 std::string varname(x.lv().data());
13 return varname.starts_with(
"__CONSTANT_");
21 std::string varname(x.lv().data());
22 varname = varname.substr(11);
23 varname[0] = varname[0] ==
'm' ?
'-' : varname[0];
24 return std::stoi(varname);
29 return f.is_binary() && f.seq(0).is_variable() &&
30 (f.sig() ==
EQ || f.sig() ==
EQUIV) &&
31 f.seq(1).is_binary() && f.seq(1).seq(0).is_variable() &&
32 f.seq(1).seq(1).is_variable();
37template <
class F,
class Env>
41 using allocator_type = battery::standard_allocator;
44 static bool is_extended_ternary_form(
const F& f) {
46 return (vars == 1 && (f.is(F::E) || (f.is_binary() && f.sig() !=
NEQ && f.sig() !=
IN && (f.seq(0).is_variable() || f.seq(1).is_variable()) && (f.seq(0).is_constant() || f.seq(1).is_constant())))) ||
47 (vars == 3 && f.is_binary() && f.sig() ==
EQ &&
48 ((f.seq(0).is_variable() && f.seq(1).is_binary() && is_ternary_op(f.seq(1).sig()) && f.seq(1).seq(0).is_variable() && f.seq(1).seq(1).is_variable())
49 || (f.seq(1).is_variable() && f.seq(0).is_binary() && is_ternary_op(f.seq(0).sig()) && f.seq(0).seq(0).is_variable() && f.seq(0).seq(1).is_variable())));
52 static bool is_ternary_op(
Sig sig) {
57 void introduce_existing_var(
const std::string& varname) {
58 if(varname.starts_with(
"__VAR_Z_")) {
59 introduced_int_vars = std::max((
unsigned int)std::stoul(varname.substr(8))+1, introduced_int_vars);
61 else if(varname.starts_with(
"__VAR_B_")) {
62 introduced_bool_vars = std::max((
unsigned int)std::stoul(varname.substr(8))+1, introduced_bool_vars);
68 Ternarizer(
const Env& env):
69 introduced_int_vars(0),
70 introduced_bool_vars(0),
71 introduced_constants(0),
75 for(
int i = 0; i < env.num_vars(); ++i) {
76 introduce_existing_var(std::string(env[i].name.data()));
82 battery::vector<F, allocator_type> conjunction;
83 battery::vector<F, allocator_type> existentials;
84 std::unordered_map<std::string, int> name2exists;
85 unsigned int introduced_int_vars;
86 unsigned int introduced_bool_vars;
87 unsigned int introduced_constants;
89 F introduce_var(
const std::string& name,
auto sort,
bool constant) {
90 auto var_name = LVar<allocator_type>(name.data());
91 assert(!env.contains(name.data()));
92 existentials.push_back(F::make_exists(
UNTYPED, var_name, sort));
93 assert(!name2exists.contains(name));
94 name2exists[name] = existentials.size() - 1;
95 if(constant) { introduced_constants++; }
96 else if(sort.is_int()) { introduced_int_vars++; }
97 else if(sort.is_bool()) { introduced_bool_vars++; }
98 return F::make_lvar(
UNTYPED, var_name);
101 F introduce_int_var() {
102 std::string name =
"__VAR_Z_" + std::to_string(introduced_int_vars);
106 F introduce_bool_var() {
107 std::string name =
"__VAR_B_" + std::to_string(introduced_bool_vars);
112 F ternarize_constant(
const F& f) {
113 assert(f.is(F::Z) || f.is(F::B));
114 auto index = f.to_z();
115 std::string name =
"__CONSTANT_" + (index < 0 ? std::string(
"m") : std::string(
"")) + std::to_string(abs(index));
117 if (name2exists.contains(name) || env.contains(name.data())) {
118 return F::make_lvar(
UNTYPED, LVar<allocator_type>(name.data()));
120 auto var = introduce_var(name, f.sort().value(),
true);
121 conjunction.push_back(F::make_binary(var,
EQ, f));
127 bool simplify_to_unary(F x, F y,
Sig sig, F z) {
135 conjunction.push_back(F::make_binary(yv,
GT, zv));
138 conjunction.push_back(F::make_binary(yv, sig, zv));
146 F push_ternary(F x, F y,
Sig sig, F z) {
147 if(simplify_to_unary(x,y,sig,z)) {
152 conjunction.push_back(F::make_binary(y,
EQ, F::make_binary(x,
ADD, z)));
156 conjunction.push_back(F::make_binary(x,
EQ, F::make_binary(y, sig, z)));
160 F ternarize_unary(
const F& f,
bool toplevel =
false) {
165 F t = push_ternary(introduce_int_var(),
ternarize(F::make_z(0)),
SUB, x);
167 return ternarize(F::make_binary(t,
NEQ, F::make_z(0)),
true);
174 F t2 = introduce_int_var();
175 compute(F::make_binary(t2,
EQ, F::make_binary(x,
MAX, t1)));
176 compute(F::make_binary(t2,
GEQ, F::make_z(0)));
178 return ternarize(F::make_binary(t2,
NEQ, F::make_z(0)),
true);
183 case NOT:
return ternarize(F::make_binary(x,
EQ, F::make_z(0)), toplevel);
186 conjunction.push_back(F::make_unary(f.sig(), x));
190 printf(
"Unary operator %s not supported\n",
string_of_sig(f.sig()));
191 printf(
"In formula: "); f.print(); printf(
"\n");
198 bool is_boolean(
const F& f) {
200 std::string varname(f.lv().data());
201 if(name2exists.contains(varname)) {
202 return battery::get<1>(existentials[name2exists[varname]].exists()).is_bool();
205 auto var_opt = env.variable_of(varname.data());
206 if(var_opt.has_value()) {
207 return var_opt->get().sort.is_bool();
217 F booleanize(
const F& t,
Sig sig) {
224 F ternarize_binary(F f,
bool toplevel) {
225 if(f.sig() ==
IN && f.is_binary() && f.seq(0).is_variable() && f.seq(1).is(F::S)) {
231 if(f.seq(1).s().size() > 1) {
232 conjunction.push_back(f);
234 return F::make_true();
246 bool almost_ternary =
false;
250 if((((f.seq(0).is_variable() || f.seq(0).is_constant()) && f.seq(1).is_binary())
251 ||((f.seq(1).is_variable() || f.seq(1).is_constant()) && f.seq(0).is_binary()))
252 && (f.sig() ==
EQUIV || f.sig() ==
EQ))
254 int left = f.seq(0).is_binary();
255 int right = f.seq(1).is_binary();
257 if(f.seq(right).sig() ==
IN) {
263 almost_ternary =
true;
266 t1 = booleanize(t1, f.sig());
268 t2 = booleanize(t2, f.sig());
269 if(!almost_ternary) {
271 if(toplevel && (f.sig() ==
NEQ || f.sig() ==
XOR || f.sig() ==
IMPLY || f.sig() ==
GT || f.sig() ==
LT)) {}
273 || ((f.sig() ==
MIN || f.sig() ==
MAX) && is_boolean(t1) && is_boolean(t2)))
275 t0 = toplevel ? ternarize_constant(F::make_z(1)) : introduce_bool_var();
278 t0 = toplevel ? ternarize_constant(F::make_z(1)) : introduce_int_var();
283 case MIN:
return push_ternary(t0, t1,
MIN, t2);
285 case MAX:
return push_ternary(t0, t1,
MAX, t2);
287 case EQUIV:
return push_ternary(t0, t1,
EQ, t2);
292 return push_ternary(ternarize_constant(F::make_z(0)), t1,
EQ, t2);
298 case LEQ:
return push_ternary(t0, t1,
LEQ, t2);
300 case GEQ:
return push_ternary(t0, t2,
LEQ, t1);
304 return push_ternary(ternarize_constant(F::make_z(0)), t1,
LEQ, t2);
312 return push_ternary(ternarize_constant(F::make_z(0)), t2,
LEQ, t1);
318 return push_ternary(t0, t1, f.sig(), t2);
323 std::pair<F, F> binarize_middle(
const F& f) {
324 assert(f.is(F::Seq) && f.seq().size() > 2);
325 battery::vector<F, allocator_type> left;
326 battery::vector<F, allocator_type> right;
328 for(i = 0; i < f.seq().size() / 2; ++i) {
329 left.push_back(f.seq(i));
331 for(; i < f.seq().size(); ++i) {
332 right.push_back(f.seq(i));
335 ternarize(left.size() == 1 ? left.back() : F::make_nary(f.sig(), std::move(left))),
336 ternarize(right.size() == 1 ? right.back() : F::make_nary(f.sig(), std::move(right)))};
339 F ternarize_nary(
const F& f,
bool toplevel) {
341 auto [t1, t2] = binarize_middle(f);
342 return ternarize(F::make_binary(t1, f.sig(), t2), toplevel);
345 F tmp =
ternarize(F::make_binary(f.seq(0), f.sig(), f.seq(1)));
346 for (
int i = 2; i < f.seq().size() - 1; i++) {
347 tmp =
ternarize(F::make_binary(tmp, f.sig(), f.seq(i)));
349 tmp =
ternarize(F::make_binary(tmp, f.sig(), f.seq(f.seq().size() - 1)), toplevel);
354 F
ternarize(
const F& f,
bool toplevel =
false) {
355 if (f.is_variable()) {
357 return ternarize(F::make_binary(f,
NEQ, F::make_z(0)),
true);
361 else if (f.is(F::Z) || f.is(F::B)) {
363 return f.to_z() != 0 ? F::make_true() : F::make_false();
365 return ternarize_constant(f);
367 else if (f.is(F::S)) {
370 else if (f.is_unary()) {
371 return ternarize_unary(f, toplevel);
373 else if (f.is_binary()) {
374 return ternarize_binary(f, toplevel);
376 else if (f.is(F::Seq) && f.seq().size() > 2) {
377 return ternarize_nary(f, toplevel);
379 printf(
"Unsupported formula: "); f.print(
false); printf(
"\n");
381 return F::make_false();
385 void compute(
const F& f) {
386 if (f.is(F::Seq) && f.sig() ==
AND) {
387 const auto& seq = f.seq();
388 for (
int i = 0; i < seq.size(); ++i) {
392 else if(f.is(F::E)) {
393 existentials.push_back(f);
394 std::string varname(battery::get<0>(f.exists()).data());
395 name2exists[varname] = existentials.size() - 1;
401 introduce_existing_var(varname);
403 else if(!f.is(F::ESeq) && !is_extended_ternary_form(f)) {
408 conjunction.push_back(f);
413 auto ternarized_formula = std::move(existentials);
414 for (
int i = 0; i < conjunction.size(); ++i) {
415 ternarized_formula.push_back(std::move(conjunction[i]));
417 if(ternarized_formula.size() == 1) {
418 return std::move(ternarized_formula[0]);
420 return F::make_nary(
AND, std::move(ternarized_formula),
UNTYPED,
false);
432template <
class F,
class Env = VarEnv<battery::standard_allocator>>
433F
ternarize(
const F& f,
const Env& env = Env(),
const std::vector<int>& constants = {}) {
434 impl::Ternarizer<F, Env> ternarizer(env);
435 for(
int c : constants) {
436 ternarizer.ternarize_constant(F::make_z(c));
438 ternarizer.compute(f);
439 return std::move(ternarizer).create();
Definition abstract_deps.hpp:14
bool is_constant_var(const F &x)
Definition ternarize.hpp:10
int value_of_constant(const F &x)
Definition ternarize.hpp:19
CUDA NI constexpr bool is_associative(Sig sig)
Definition ast.hpp:238
@ XOR
Logical connector.
Definition ast.hpp:114
@ NEQ
Equality relations.
Definition ast.hpp:112
@ LEQ
Unary arithmetic function symbols.
Definition ast.hpp:113
@ NOT
Unary arithmetic function symbols.
Definition ast.hpp:114
@ IMPLY
Unary arithmetic function symbols.
Definition ast.hpp:114
@ OR
Unary arithmetic function symbols.
Definition ast.hpp:114
@ EQ
Unary arithmetic function symbols.
Definition ast.hpp:112
@ GEQ
Unary arithmetic function symbols.
Definition ast.hpp:113
@ ADD
Unary arithmetic function symbols.
Definition ast.hpp:97
@ IN
Set membership predicate.
Definition ast.hpp:108
@ MIN
Unary arithmetic function symbols.
Definition ast.hpp:97
@ GT
Arithmetic comparison predicates. When applied to set, it corresponds to the lexicographic ordering o...
Definition ast.hpp:113
@ MAX
Binary arithmetic function symbols.
Definition ast.hpp:97
@ LT
Unary arithmetic function symbols.
Definition ast.hpp:113
@ AND
Unary arithmetic function symbols.
Definition ast.hpp:114
@ MAXIMIZE
Unary "meta-predicate" indicating that its argument must be maximized, according to the increasing or...
Definition ast.hpp:116
@ MINIMIZE
Same as MAXIMIZE, but for minimization.
Definition ast.hpp:117
@ EQUIV
Unary arithmetic function symbols.
Definition ast.hpp:114
@ ABS
Unary arithmetic function symbols.
Definition ast.hpp:96
@ SUB
Unary arithmetic function symbols.
Definition ast.hpp:97
@ NEG
Unary arithmetic function symbols.
Definition ast.hpp:96
CUDA NI constexpr bool is_predicate(Sig sig)
Definition ast.hpp:256
CUDA NI const char * string_of_sig(Sig sig)
Definition ast.hpp:121
CUDA NI int num_vars(const F &f)
Definition algorithm.hpp:154
F ternarize(const F &f, const Env &env=Env(), const std::vector< int > &constants={})
Definition ternarize.hpp:433
CUDA NI constexpr bool is_logical(Sig sig)
Definition ast.hpp:252
CUDA bool is_tnf(const F &f)
Definition ternarize.hpp:28
CUDA F decompose_in_constraint(const F &f, const typename F::allocator_type &alloc=typename F::allocator_type())
Definition algorithm.hpp:944
#define UNTYPED
Definition sort.hpp:21
@ Bool
Definition sort.hpp:28
@ Int
Definition sort.hpp:29