1#ifndef LALA_CORE_TERNARIZE_HPP
2#define LALA_CORE_TERNARIZE_HPP
10template <
class F,
class Env>
14 using allocator_type = battery::standard_allocator;
17 static bool is_ternary_form(
const F& f,
bool ternarize_all =
false) {
19 return (!ternarize_all && vars == 1) ||
20 (vars == 3 && f.is_binary() && f.sig() ==
EQ &&
21 ((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())
22 || (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())));
25 static bool is_ternary_op(
Sig sig) {
30 void introduce_existing_var(
const std::string& varname) {
31 if(varname.starts_with(
"__VAR_Z_")) {
32 introduced_int_vars = std::max((
unsigned int)std::stoul(varname.substr(8))+1, introduced_int_vars);
34 else if(varname.starts_with(
"__VAR_B_")) {
35 introduced_bool_vars = std::max((
unsigned int)std::stoul(varname.substr(8))+1, introduced_bool_vars);
41 Ternarizer(
const Env& env,
bool ternarize_all):
42 introduced_int_vars(0),
43 introduced_bool_vars(0),
44 introduced_constants(0),
46 ternarize_all(ternarize_all)
49 for(
int i = 0; i < env.num_vars(); ++i) {
50 introduce_existing_var(std::string(env[i].name.data()));
57 battery::vector<F, allocator_type> conjunction;
58 battery::vector<F, allocator_type> existentials;
59 std::unordered_map<std::string, int> name2exists;
60 unsigned int introduced_int_vars;
61 unsigned int introduced_bool_vars;
62 unsigned int introduced_constants;
64 F introduce_var(
const std::string& name,
auto sort,
bool constant) {
66 assert(!env.contains(name.data()));
67 existentials.push_back(F::make_exists(
UNTYPED, var_name, sort));
68 assert(!name2exists.contains(name));
69 name2exists[name] = existentials.size() - 1;
70 if(constant) { introduced_constants++; }
71 else if(sort.is_int()) { introduced_int_vars++; }
72 else if(sort.is_bool()) { introduced_bool_vars++; }
73 return F::make_lvar(
UNTYPED, var_name);
76 F introduce_int_var() {
77 std::string name =
"__VAR_Z_" + std::to_string(introduced_int_vars);
81 F introduce_bool_var() {
82 std::string name =
"__VAR_B_" + std::to_string(introduced_bool_vars);
86 F ternarize_constant(
const F& f) {
87 assert(f.is(F::Z) || f.is(F::B));
88 auto index = f.to_z();
89 std::string name =
"__CONSTANT_" + (index < 0 ? std::string(
"m") : std::string(
"")) + std::to_string(abs(index));
91 if (name2exists.contains(name) || env.contains(name.data())) {
92 return F::make_lvar(UNTYPED, LVar<allocator_type>(name.data()));
94 auto var = introduce_var(name, f.sort().value(),
true);
95 conjunction.push_back(F::make_binary(var,
EQ, f));
99 bool is_constant_var(
const F& x)
const {
101 std::string varname(x.lv().data());
102 return varname.starts_with(
"__CONSTANT_");
107 int value_of_constant(
const F& x)
const {
108 assert(is_constant_var(x));
109 std::string varname(x.lv().data());
110 varname = varname.substr(11);
111 varname[0] = varname[0] ==
'm' ?
'-' : varname[0];
112 return std::stoi(varname);
116 bool try_simplify_push_ternary(
const F& x,
const F& y,
Sig sig,
const F& z) {
122 int xc = is_constant_var(x);
123 int yc = is_constant_var(y);
124 int zc = is_constant_var(z);
126 F y_sig_z = F::make_binary(F::make_z(value_of_constant(y)), sig, F::make_z(value_of_constant(z)));
127 F simplified = F::make_binary(x,
EQ,
eval(y_sig_z));
128 if(is_ternary_form(simplified)) {
133 else if(xc + yc + zc == 2) {
137 ? F::make_binary(F::make_z(value_of_constant(y)), sig, z)
138 : F::make_binary(y, sig, F::make_z(value_of_constant(z)));
139 int x_value = value_of_constant(x);
142 F not_y_sig_z = r.has_value() ? *r : F::make_unary(
NOT, y_sig_z);
143 not_y_sig_z =
eval(not_y_sig_z);
144 if(is_ternary_form(not_y_sig_z)) {
145 compute(not_y_sig_z);
149 else if(is_ternary_form(y_sig_z)) {
158 F push_ternary(
const F& x,
const F& y,
Sig sig,
const F& z) {
159 if(try_simplify_push_ternary(x, y, sig, z)) {
163 conjunction.push_back(F::make_binary(x,
EQ, F::make_binary(y, sig, z)));
167 F ternarize_unary(
const F& f,
bool toplevel =
false) {
172 F t = push_ternary(introduce_int_var(),
ternarize(F::make_z(0)),
SUB, x);
174 return ternarize(F::make_binary(t,
NEQ, F::make_z(0)),
true);
181 F t2 = introduce_int_var();
182 compute(F::make_binary(t2,
GEQ, F::make_z(0)));
183 compute(F::make_binary(t2,
GEQ, t1));
184 compute(F::make_binary(t2,
GEQ, x));
185 compute(F::make_binary(t2,
LEQ, F::make_binary(x,
MAX, t1)));
187 return ternarize(F::make_binary(t2,
NEQ, F::make_z(0)),
true);
192 case NOT:
return ternarize(F::make_binary(x,
EQ, F::make_z(0)), toplevel);
195 conjunction.push_back(F::make_unary(f.sig(), x));
199 printf(
"Unary operator %s not supported\n",
string_of_sig(f.sig()));
200 printf(
"In formula: "); f.print(); printf(
"\n");
207 bool is_boolean(
const F& f) {
209 std::string varname(f.lv().data());
210 if(name2exists.contains(varname)) {
211 return battery::get<1>(existentials[name2exists[varname]].exists()).is_bool();
214 auto var_opt = env.variable_of(varname.data());
215 if(var_opt.has_value()) {
216 return var_opt->get().sort.is_bool();
226 F booleanize(
const F& t,
Sig sig) {
233 F ternarize_binary(F f,
bool toplevel) {
240 bool almost_ternary =
false;
244 if((((f.seq(0).is_variable() || f.seq(0).is_constant()) && f.seq(1).is_binary())
245 ||((f.seq(1).is_variable() || f.seq(1).is_constant()) && f.seq(0).is_binary()))
246 && (f.sig() ==
EQUIV || f.sig() ==
EQ))
248 int left = f.seq(0).is_binary();
249 int right = f.seq(1).is_binary();
253 almost_ternary =
true;
256 t1 = booleanize(t1, f.sig());
258 t2 = booleanize(t2, f.sig());
259 if(!almost_ternary) {
261 if(toplevel && (f.sig() ==
NEQ || f.sig() ==
XOR || f.sig() ==
IMPLY || f.sig() ==
GT || f.sig() ==
LT)) {}
263 || ((f.sig() ==
MIN || f.sig() ==
MAX) && is_boolean(t1) && is_boolean(t2)))
265 t0 = toplevel ? ternarize_constant(F::make_z(1)) : introduce_bool_var();
268 t0 = toplevel ? ternarize_constant(F::make_z(1)) : introduce_int_var();
273 case MIN:
return push_ternary(t0, t1,
MIN, t2);
275 case MAX:
return push_ternary(t0, t1,
MAX, t2);
277 case EQUIV:
return push_ternary(t0, t1,
EQ, t2);
282 return push_ternary(ternarize_constant(F::make_z(0)), t1,
EQ, t2);
288 case LEQ:
return push_ternary(t0, t1,
LEQ, t2);
290 case GEQ:
return push_ternary(t0, t2,
LEQ, t1);
294 return push_ternary(ternarize_constant(F::make_z(0)), t1,
LEQ, t2);
302 return push_ternary(ternarize_constant(F::make_z(0)), t2,
LEQ, t1);
308 return push_ternary(t0, t1, f.sig(), t2);
313 std::pair<F, F> binarize_middle(
const F& f) {
314 assert(f.is(F::Seq) && f.seq().size() > 2);
315 battery::vector<F, allocator_type> left;
316 battery::vector<F, allocator_type> right;
318 for(i = 0; i < f.seq().size() / 2; ++i) {
319 left.push_back(f.seq(i));
321 for(; i < f.seq().size(); ++i) {
322 right.push_back(f.seq(i));
325 ternarize(left.size() == 1 ? left.back() : F::make_nary(f.sig(), std::move(left))),
326 ternarize(right.size() == 1 ? right.back() : F::make_nary(f.sig(), std::move(right)))};
329 F ternarize_nary(
const F& f,
bool toplevel) {
331 auto [t1, t2] = binarize_middle(f);
332 return ternarize(F::make_binary(t1, f.sig(), t2), toplevel);
335 F tmp =
ternarize(F::make_binary(f.seq(0), f.sig(), f.seq(1)));
336 for (
int i = 2; i < f.seq().size() - 1; i++) {
337 tmp =
ternarize(F::make_binary(tmp, f.sig(), f.seq(i)));
339 tmp =
ternarize(F::make_binary(tmp, f.sig(), f.seq(f.seq().size() - 1)), toplevel);
344 F
ternarize(
const F& f,
bool toplevel =
false) {
345 if (f.is_variable()) {
347 return ternarize(F::make_binary(f,
NEQ, F::make_z(0)),
true);
351 else if (f.is(F::Z) || f.is(F::B)) {
353 return f.to_z() != 0 ? F::make_true() : F::make_false();
355 return ternarize_constant(f);
357 else if (f.is(F::S)) {
360 else if (f.is_unary()) {
361 return ternarize_unary(f, toplevel);
363 else if (f.is_binary()) {
364 return ternarize_binary(f, toplevel);
366 else if (f.is(F::Seq) && f.seq().size() > 2) {
367 return ternarize_nary(f, toplevel);
369 printf(
"Unsupported formula: "); f.print(
false); printf(
"\n");
371 return F::make_false();
375 void compute(
const F& f) {
376 if (f.is(F::Seq) && f.sig() ==
AND) {
378 for (
int i = 0; i < seq.size(); ++i) {
382 else if(f.is(F::E)) {
383 existentials.push_back(f);
384 std::string varname(battery::get<0>(f.exists()).data());
385 name2exists[varname] = existentials.size() - 1;
391 introduce_existing_var(varname);
393 else if(!f.is(F::ESeq) && !is_ternary_form(f, ternarize_all)) {
398 conjunction.push_back(f);
403 auto ternarized_formula = std::move(existentials);
404 for (
int i = 0; i < conjunction.size(); ++i) {
405 ternarized_formula.push_back(std::move(conjunction[i]));
407 if(ternarized_formula.size() == 1) {
408 return std::move(ternarized_formula[0]);
410 return F::make_nary(
AND, std::move(ternarized_formula),
UNTYPED,
false);
422template <
class F,
class Env = VarEnv<battery::standard_allocator>>
423F
ternarize(
const F& f,
const Env& env = Env(),
bool ternarize_all =
false) {
424 impl::Ternarizer<F, Env> ternarizer(env, ternarize_all);
425 ternarizer.compute(f);
426 return std::move(ternarizer).create();
Definition abstract_deps.hpp:14
CUDA NI constexpr bool is_associative(Sig sig)
Definition ast.hpp:204
@ 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
@ 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 std::optional< F > negate(const F &f)
Definition algorithm.hpp:284
battery::string< Allocator > LVar
Definition ast.hpp:25
CUDA NI constexpr bool is_predicate(Sig sig)
Definition ast.hpp:217
CUDA NI const char * string_of_sig(Sig sig)
Definition ast.hpp:121
CUDA NI int num_vars(const F &f)
Definition algorithm.hpp:153
CUDA NI constexpr bool is_logical(Sig sig)
Definition ast.hpp:213
F ternarize(const F &f, const Env &env=Env(), bool ternarize_all=false)
Definition ternarize.hpp:423
CUDA NI F eval(const F &f)
Definition algorithm.hpp:792
#define UNTYPED
Definition sort.hpp:21
@ Bool
Definition sort.hpp:28
@ Int
Definition sort.hpp:29