48 template <
class Alloc>
65 template <
class SnapshotType>
66 CUDA
snapshot_type(
const SnapshotType& other,
const Alloc& alloc = Alloc{})
75 constexpr static const bool sequential = sub_type::sequential;
84 constexpr static const char*
name =
"PC";
86 template <
class A2,
class Alloc2>
92 template<
class Alloc>
using term_ptr = battery::unique_ptr<pc::Term<A, Alloc>, Alloc>;
96 using props_type = battery::vector<formula_type, allocator_type>;
97 battery::root_ptr<props_type, allocator_type> props;
100 template <
class Alloc>
101 using formula_seq = battery::vector<pc::Formula<A, Alloc>, Alloc>;
103 template <
class Alloc>
104 using term_seq = battery::vector<pc::Term<A, Alloc>, Alloc>;
106 template <
class Alloc,
class SubType>
122 template <
class InterpretedType>
128 template <
class Alloc2,
class SubType2>
132 template <
class Alloc>
135 template <
class Alloc>
139 : atype(atype), sub(std::move(sub))
140 , props(battery::allocate_root<props_type,
allocator_type>(alloc, alloc)) {}
144 , props(std::move(other.props))
145 , sub(std::move(other.sub))
151 template<
class A2,
class Alloc2,
class... Allocators>
152 CUDA
static battery::root_ptr<props_type, allocator_type> init_props(
const PC<A2, Alloc2>& other, AbstractDeps<Allocators...>& deps) {
154 if constexpr(std::is_same_v<allocator_type, Alloc2>) {
155 if(deps.is_shared_copy()) {
159 auto r = battery::allocate_root<props_type, allocator_type>(alloc, *(other.props), alloc);
164 template<
class A2,
class Alloc2,
class... Allocators>
167 , sub(deps.template clone<A>(other.sub))
168 , props(init_props(other, deps))
172 return props.get_allocator();
180 AType atype_sub = UNTYPED,
184 return PC{atype, battery::allocate_shared<sub_type>(sub_alloc, sub_type::bot(atype_sub, sub_alloc)), alloc};
189 AType atype_sub = UNTYPED,
193 return PC{atype, battery::allocate_shared<sub_type>(sub_alloc, sub_type::top(atype_sub, sub_alloc)), alloc};
201 AType atype_sub = env.extends_abstract_dom();
202 AType atype = env.extends_abstract_dom();
203 return bot(atype, atype_sub, alloc, sub_alloc);
211 AType atype_sub = env.extends_abstract_dom();
212 AType atype = env.extends_abstract_dom();
213 return top(atype, atype_sub, alloc, sub_alloc);
217 template <
bool diagnose,
class F,
class Alloc>
218 CUDA
bool interpret_unary(
const F& f,
term_seq<Alloc>& intermediate, IDiagnostics& diagnostics, term_ptr<Alloc>&& x)
const {
220 Alloc alloc = x.get_allocator();
222 case NEG: intermediate.push_back(T::make_neg(std::move(x)));
break;
223 case ABS: intermediate.push_back(T::make_abs(std::move(x)));
break;
224 default: RETURN_INTERPRETATION_ERROR(
"Unsupported unary symbol in a term.");
229 template <
bool diagnose,
class F,
class Alloc>
230 CUDA
bool interpret_binary(
const F& f,
term_seq<Alloc>& intermediate, IDiagnostics& diagnostics, term_ptr<Alloc>&& x, term_ptr<Alloc>&& y)
const
232 using T = pc::Term<A, Alloc>;
234 case ADD: intermediate.push_back(T::make_add(std::move(x), std::move(y)));
break;
235 case SUB: intermediate.push_back(T::make_sub(std::move(x), std::move(y)));
break;
236 case MUL: intermediate.push_back(T::make_mul(std::move(x), std::move(y)));
break;
237 case TDIV: intermediate.push_back(T::make_tdiv(std::move(x), std::move(y)));
break;
238 case FDIV: intermediate.push_back(T::make_fdiv(std::move(x), std::move(y)));
break;
239 case CDIV: intermediate.push_back(T::make_cdiv(std::move(x), std::move(y)));
break;
240 case EDIV: intermediate.push_back(T::make_ediv(std::move(x), std::move(y)));
break;
241 case MIN: intermediate.push_back(T::make_min(std::move(x), std::move(y)));
break;
242 case MAX: intermediate.push_back(T::make_max(std::move(x), std::move(y)));
break;
243 default: RETURN_INTERPRETATION_ERROR(
"Unsupported binary symbol in a term.");
248 template <
bool diagnose,
class F,
class Alloc>
251 using T = pc::Term<A, Alloc>;
253 case ADD: intermediate.push_back(T::make_naryadd(std::move(operands)));
break;
254 case MUL: intermediate.push_back(T::make_narymul(std::move(operands)));
break;
255 default: RETURN_INTERPRETATION_ERROR(
"Unsupported nary symbol in a term.");
260 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
261 CUDA
bool interpret_sequence(
const F& f, Env& env,
term_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const
263 using T = pc::Term<A, Alloc>;
264 Alloc alloc = intermediate.get_allocator();
267 for(
int i = 0; i < f.seq().size(); ++i) {
269 if(!interpret_term<kind, diagnose>(f.seq(i), env, subterms, diagnostics)) {
270 if(!interpret_formula<kind, diagnose>(f.seq(i), env, subformulas, diagnostics,
true)) {
274 subterms.push_back(T::make_formula(
275 battery::allocate_unique<pc::Formula<A, Alloc>>(alloc, std::move(subformulas.back()))));
279 if(subterms.size() == 1) {
280 return interpret_unary<diagnose>(f,
283 battery::allocate_unique<T>(alloc, std::move(subterms[0])));
285 else if(subterms.size() == 2) {
286 return interpret_binary<diagnose>(f,
289 battery::allocate_unique<T>(alloc, std::move(subterms[0])),
290 battery::allocate_unique<T>(alloc, std::move(subterms[1])));
293 return interpret_nary<diagnose>(f,
296 std::move(subterms));
300 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
301 CUDA
bool interpret_term(
const F& f, Env& env,
term_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
302 using T = pc::Term<A, Alloc>;
303 using F2 = TFormula<Alloc>;
304 if(f.is_variable()) {
307 intermediate.push_back(T::make_var(avar));
314 else if(f.is_constant()) {
315 auto constant = F2::make_binary(F2::make_avar(AVar{}), EQ, f, UNTYPED, intermediate.get_allocator());
318 intermediate.push_back(T::make_constant(std::move(k)));
322 RETURN_INTERPRETATION_ERROR(
"Constant in a term could not be interpreted in the underlying abstract universe.");
325 else if(f.is(F::Seq)) {
326 return interpret_sequence<kind, diagnose>(f, env, intermediate, diagnostics);
329 RETURN_INTERPRETATION_ERROR(
"The shape of the formula is not supported in PC, and could not be interpreted as a term.");
333 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
334 CUDA
bool interpret_negation(
const F& f, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics,
bool neg_context)
const {
337 return interpret_formula<kind, diagnose>(*nf, env, intermediate, diagnostics, neg_context);
340 RETURN_INTERPRETATION_ERROR(
"We must query this formula for disentailement, but we could not compute its negation.");
344 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc,
class Create>
345 CUDA
bool interpret_binary_logical_connector(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics,
bool neg_context, Create&& create)
const {
346 using PF = pc::Formula<A, Alloc>;
347 Alloc alloc = intermediate.get_allocator();
349 if( interpret_formula<kind, diagnose>(f, env, operands, diagnostics, neg_context)
350 && interpret_formula<kind, diagnose>(g, env, operands, diagnostics, neg_context))
352 intermediate.push_back(create(
353 battery::allocate_unique<PF>(alloc, std::move(operands[0])),
354 battery::allocate_unique<PF>(alloc, std::move(operands[1]))));
362 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
363 CUDA
bool interpret_conjunction(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics,
bool neg_context)
const {
364 using PF = pc::Formula<A, Alloc>;
365 return interpret_binary_logical_connector<kind, diagnose>(f, g, env, intermediate, diagnostics, neg_context,
366 [](
auto&& l,
auto&& k) {
return PF::make_conj(std::move(l), std::move(k)); });
369 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
370 CUDA
bool interpret_disjunction(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
371 using PF = pc::Formula<A, Alloc>;
372 return interpret_binary_logical_connector<kind, diagnose>(f, g, env, intermediate, diagnostics,
true,
373 [](
auto&& l,
auto&& k) {
return PF::make_disj(std::move(l), std::move(k)); });
376 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
377 CUDA
bool interpret_biconditional(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
378 using PF = pc::Formula<A, Alloc>;
379 return interpret_binary_logical_connector<kind, diagnose>(f, g, env, intermediate, diagnostics,
true,
380 [](
auto&& l,
auto&& k) {
return PF::make_bicond(std::move(l), std::move(k)); });
383 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
384 CUDA
bool interpret_implication(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
385 using PF = pc::Formula<A, Alloc>;
386 return interpret_binary_logical_connector<kind, diagnose>(f, g, env, intermediate, diagnostics,
true,
387 [](
auto&& l,
auto&& k) {
return PF::make_imply(std::move(l), std::move(k)); });
390 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
391 CUDA
bool interpret_xor(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
392 using PF = pc::Formula<A, Alloc>;
393 return interpret_binary_logical_connector<kind, diagnose>(f, g, env, intermediate, diagnostics,
true,
394 [](
auto&& l,
auto&& k) {
return PF::make_xor(std::move(l), std::move(k)); });
397 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc,
class Create>
398 CUDA
bool interpret_binary_predicate(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics, Create&& create)
const
400 using PF = pc::Formula<A, Alloc>;
401 using T = pc::Term<A, Alloc>;
402 Alloc alloc = intermediate.get_allocator();
404 if( interpret_term<kind, diagnose>(f, env, operands, diagnostics)
405 && interpret_term<kind, diagnose>(g, env, operands, diagnostics))
407 intermediate.push_back(create(std::move(operands[0]), std::move(operands[1])));
415 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
416 CUDA
bool interpret_equality(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
417 using PF = pc::Formula<A, Alloc>;
418 return interpret_binary_predicate<kind, diagnose>(f, g, env, intermediate, diagnostics,
419 [](
auto&& l,
auto&& k) {
return PF::make_eq(std::move(l), std::move(k)); });
422 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
423 CUDA
bool interpret_disequality(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
424 using PF = pc::Formula<A, Alloc>;
425 return interpret_binary_predicate<kind, diagnose>(f, g, env, intermediate, diagnostics,
426 [](
auto&& l,
auto&& k) {
return PF::make_neq(std::move(l), std::move(k)); });
429 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
430 CUDA
bool interpret_inequalityLEQ(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
431 using PF = pc::Formula<A, Alloc>;
432 return interpret_binary_predicate<kind, diagnose>(f, g, env, intermediate, diagnostics,
433 [](
auto&& l,
auto&& k) {
return PF::make_leq(std::move(l), std::move(k)); });
436 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
437 CUDA
bool interpret_inequalityGT(
const F& f,
const F& g, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
438 using PF = pc::Formula<A, Alloc>;
439 return interpret_binary_predicate<kind, diagnose>(f, g, env, intermediate, diagnostics,
440 [](
auto&& l,
auto&& k) {
return PF::make_gt(std::move(l), std::move(k)); });
443 template <
bool neg,
bool diagnose,
class F,
class Env,
class Alloc>
444 CUDA
bool interpret_literal(
const F& f, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
445 using PF = pc::Formula<A, Alloc>;
449 intermediate.push_back(PF::make_nvarlit(avar));
452 intermediate.push_back(PF::make_pvarlit(avar));
460 template <
class F,
class Alloc>
461 CUDA F itv_to_formula(AType ty,
const F& f,
const battery::tuple<F, F>& itv,
const Alloc& alloc)
const {
462 using F2 = TFormula<Alloc>;
463 if(battery::get<0>(itv) == battery::get<1>(itv)) {
464 return F2::make_binary(f, EQ, battery::get<0>(itv), ty, alloc);
469 F2::make_binary(f, GEQ, battery::get<0>(itv), ty, alloc),
471 F2::make_binary(f, LEQ, battery::get<1>(itv), ty, alloc),
477 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
478 CUDA
bool interpret_in_decomposition(
const F& f, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics,
bool neg_context =
false)
const {
479 assert(f.seq(1).is(F::S));
480 using F2 = TFormula<Alloc>;
481 Alloc alloc = intermediate.get_allocator();
483 const auto& set = f.seq(1).s();
484 if(set.size() == 1) {
485 return interpret_formula<kind, diagnose>(
486 itv_to_formula(f.type(), f.seq(0), set[0], alloc),
487 env, intermediate, diagnostics, neg_context);
490 typename F2::Sequence disjunction =
typename F2::Sequence(alloc);
491 disjunction.reserve(set.size());
492 for(
size_t i = 0; i < set.size(); ++i) {
493 disjunction.push_back(itv_to_formula(f.type(), f.seq(0), set[i], alloc));
495 return interpret_formula<kind, diagnose>(
496 F2::make_nary(OR, std::move(disjunction), f.type()),
497 env, intermediate, diagnostics, neg_context);
502 CUDA F binarize(
const F& f,
size_t i)
const {
503 assert(f.is(F::Seq) && f.seq().size() >= 2);
504 if(i + 2 == f.seq().size()) {
505 return F::make_binary(f.seq(i), f.sig(), f.seq(i+1), f.type(), f.seq().get_allocator(),
false);
508 return F::make_binary(f.seq(i), f.sig(), binarize(f, i+1), f.type(), f.seq().get_allocator(),
false);
512 template <
bool diagnose,
class F,
class Env,
class Alloc>
513 CUDA
bool interpret_abstract_element(
const F& f, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics)
const {
515 if(!nf.has_value()) {
516 RETURN_INTERPRETATION_ERROR(
"We must query this formula for disentailement, but we could not compute its negation.");
520 typename A::template
tell_type<Alloc> not_tellv(intermediate.get_allocator());
522 typename A::template
tell_type<Alloc> not_askv(intermediate.get_allocator());
528 using PF = pc::Formula<A, Alloc>;
529 intermediate.push_back(PF::make_abstract_element(std::move(tellv), std::move(not_tellv), std::move(askv), std::move(not_askv)));
539 template <IKind kind,
bool diagnose,
class F,
class Env,
class Alloc>
540 CUDA
bool interpret_formula(
const F& f, Env& env,
formula_seq<Alloc>& intermediate, IDiagnostics& diagnostics,
bool neg_context =
false)
const {
541 using PF = pc::Formula<A, Alloc>;
542 using F2 = TFormula<Alloc>;
543 Alloc alloc = intermediate.get_allocator();
544 if(f.type() !=
aty() && !f.is_untyped() && !f.is_variable()) {
546 RETURN_INTERPRETATION_ERROR(
"The type of the formula does not match the type of this abstract domain.");
548 return interpret_abstract_element<diagnose>(f, env, intermediate, diagnostics);
551 intermediate.push_back(PF::make_false());
554 else if(f.is_true()) {
555 intermediate.push_back(PF::make_true());
559 else if(f.is(F::Seq) && f.seq().size() == 1 && f.sig() == NOT &&
560 f.seq(0).is_variable())
562 return interpret_literal<true, diagnose>(f.seq(0), env, intermediate, diagnostics);
565 else if(f.is_variable()) {
566 return interpret_literal<false, diagnose>(f, env, intermediate, diagnostics);
569 else if(f.is(F::Seq) && f.seq().size() == 1 && f.sig() == NOT) {
570 return interpret_negation<kind, diagnose>(f.seq(0), env, intermediate, diagnostics, neg_context);
573 else if(f.is(F::Seq) && f.sig() == IN) {
574 return interpret_in_decomposition<kind, diagnose>(f, env, intermediate, diagnostics, neg_context);
577 else if(f.is(F::Seq) && f.seq().size() > 2 && (f.sig() == AND || f.sig() == OR || f.sig() == EQUIV)) {
578 return interpret_formula<kind, diagnose>(binarize(f,0), env, intermediate, diagnostics, neg_context);
580 else if(f.is_binary()) {
583 case AND:
return interpret_conjunction<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics, neg_context);
584 case OR:
return interpret_disjunction<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
585 case EQUIV:
return interpret_biconditional<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
586 case IMPLY:
return interpret_implication<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
587 case XOR:
return interpret_xor<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
590 if(f.seq(0).is_logical() || f.seq(1).is_logical()) {
591 return interpret_biconditional<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
594 return interpret_equality<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
597 case NEQ:
return interpret_disequality<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
598 case LEQ:
return interpret_inequalityLEQ<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
599 case GT:
return interpret_inequalityGT<kind, diagnose>(f.seq(0), f.seq(1), env, intermediate, diagnostics);
600 case GEQ:
return interpret_inequalityLEQ<kind, diagnose>(f.seq(1), f.seq(0), env, intermediate, diagnostics);
601 case LT:
return interpret_inequalityGT<kind, diagnose>(f.seq(1), f.seq(0), env, intermediate, diagnostics);
603 RETURN_INTERPRETATION_ERROR(
"Unsupported binary symbol in a formula.");
606 RETURN_INTERPRETATION_ERROR(
"The shape of this formula is not supported.");
610 template <IKind kind,
bool diagnose = false,
class F,
class Env,
class I>
611 CUDA NI
bool interpret(
const F& f, Env& env, I& intermediate, IDiagnostics& diagnostics)
const {
612 size_t error_context = 0;
613 if constexpr(diagnose) {
614 diagnostics.add_suberror(IDiagnostics(
false,
name,
"Uninterpretable formula in both PC and its sub-domain.", f));
615 error_context = diagnostics.num_suberrors();
618 AType current = f.type();
619 const_cast<F&
>(f).type_as(sub->aty());
624 if(!(f.is_binary() && f.sig() == IN && f.seq(0).is_variable() && f.seq(1).is(F::S) && f.seq(1).s().size() > 1)) {
628 res = universe_type::preserve_join;
631 const_cast<F&
>(f).type_as(current);
633 res = interpret_formula<kind, diagnose>(f, env, intermediate.props, diagnostics);
635 if constexpr(diagnose) {
636 diagnostics.merge(res, error_context);
643 template <
bool diagnose = false,
class F,
class Env,
class Alloc2>
648 template <
bool diagnose = false,
class F,
class Env,
class Alloc2>
659 template <
class Alloc2>
661 local::B has_changed = sub->deduce(t.
sub_value);
662 if(t.
props.size() > 0) {
663 auto& props2 = *props;
664 size_t n = props2.size();
665 props2.reserve(n + t.
props.size());
666 for(
int i = 0; i < t.
props.size(); ++i) {
669 battery::vector<size_t> lengths(props2.size());
670 for(
int i = 0; i < props2.size(); ++i) {
671 lengths[i] = props2[i].length();
673 battery::sorti(props2,
675 return props2[i].kind() < props2[j].kind() || (props2[i].kind() == props2[j].kind() && lengths[i] < lengths[j]);
683 return sub->embed(x, dom);
686 template <
class Alloc2>
688 for(
int i = 0; i < t.
props.size(); ++i) {
689 if(!t.
props[i].ask(*sub)) {
696 CUDA local::B
ask(
size_t i)
const {
697 return (*props)[i].ask(*sub);
701 return sub->num_deductions() + props->size();
706 if(
is_top()) {
return false; }
708 return sub->deduce(i);
711 return (*props)[i - sub->num_deductions()].deduce(*sub);
719 return sub->is_bot();
724 return sub->is_top() && props->size() == 0;
732 return sub->project(x);
735 template <
class Univ>
744 template <
class Alloc2 = allocator_type>
749 template <
class Alloc2>
751 size_t n = props->size();
752 for(
size_t i = snap.
num_props; i < n; ++i) {
759 template <
class ExtractionStrategy = NonAtomicExtraction>
760 CUDA
bool is_extractable(
const ExtractionStrategy& strategy = ExtractionStrategy())
const {
764 for(
int i = 0; i < props->size(); ++i) {
769 return sub->is_extractable(strategy);
778 if constexpr(impl::is_pc_like<B>::value) {
779 sub->extract(*ua.sub);
786 template<
class Env,
class Allocator2 =
typename Env::allocator_type>
787 CUDA NI TFormula<Allocator2>
deinterpret(
const Env& env, Allocator2 allocator = Allocator2())
const {
788 using F = TFormula<Allocator2>;
789 typename F::Sequence seq{allocator};
790 seq.push_back(sub->deinterpret(env, allocator));
791 for(
int i = 0; i < props->size(); ++i) {
792 seq.push_back((*props)[i].
deinterpret(*sub, env,
aty(), allocator));
794 return F::make_nary(AND, std::move(seq),
aty());
797 template<
class I,
class Env,
class Allocator2 =
typename Env::allocator_type>
798 CUDA NI TFormula<Allocator2>
deinterpret(
const I& intermediate,
const Env& env, Allocator2 allocator = Allocator2())
const {
799 using F = TFormula<Allocator2>;
800 typename F::Sequence seq{allocator};
801 seq.push_back(sub->deinterpret(intermediate.sub_value, env, allocator));
802 for(
int i = 0; i < intermediate.props.size(); ++i) {
803 seq.push_back(intermediate.props[i].deinterpret(*sub, env,
aty(), allocator));
805 return F::make_nary(AND, std::move(seq),
aty());