PreTriggers #
Triggers are one of the most essential definitions for the chase. They are our primary way for modelling specific applications of rules.
Quite simply, a trigger is just a pair of a rule and a substitution that tells us how variables should be replaced.
For actual triggers, we will require a way to tell if they should still be applied or not. We refer to this with the notion of "obsoleteness" later.
However, most of the machinery around triggers can be introduced agnostic of any kind of obsoleteness.
Consequently, we call the "almost trigger" a PreTrigger.
A trigger is self contained in the sense that it "knows" what its result will be independant of the chase context. This would not be so simple if we considered nulls instead of Skolem terms.
A PreTrigger is nothing more than a pair of a Rule and a GroundSubstitution.
- rule : Rule sig
- subs : GroundSubstitution sig
Instances For
In the context of a trigger, we Skolemize a VarOrConst by passing the rule id and frontier of the rule in the trigger.
Equations
- trg.skolemize_var_or_const disjunctIndex var_or_const = VarOrConst.skolemize trg.rule.id disjunctIndex trg.rule.frontier var_or_const
Instances For
We apply a trigger to a VarOrConst by Skolemizing and then applying the GroundSubstitution from the trigger. We need this to define the trigger result later.
Equations
- trg.apply_to_var_or_const disjunctIndex = trg.subs.apply_skolem_term ∘ trg.skolemize_var_or_const disjunctIndex
Instances For
Applying a trigger to a constant changes nothing.
Applying a trigger to a frontier variable does not skolemize but merely applied the substitution.
A shortcut definition for how the Skolem term resulting from applying the trigger to an existential variable will look.
Equations
Instances For
Applying a trigger to a non-frontier variable, yields exactly the Skolem function term from the shortcup definition functional_term_for_var.
For non-frontier variable, applyign the trigger is injective.
Applying the trigger to non-frontier variables yields a term that cannot possibly be in the mapped frontier. In other words, terms for existential variables are fresh (although freshness entails more than that).
We lift the trigger application from VarOrConst to FunctionFreeAtom.
Equations
- trg.apply_to_function_free_atom disjunctIndex atom = (trg.apply_to_var_or_const disjunctIndex).apply_generalized_atom atom
Instances For
The body does not feature any existential variables. Therefore, we mapped body merely results from applying the trigger's substitution to the body of its rule.
Equations
- trg.mapped_body = trg.subs.apply_function_free_conj trg.rule.body
Instances For
A term occurs in mapped_body if and only if it is a constant in the rule body of if there exists a variable in the rule body that is mapped to the term by the substitution.
The mapped head is the result of the trigger and is simply the application to all head atoms. This result has a list of result facts for each of the head disjuncts. Note again that existential variables are Skolemized before the trigger's substitution is applied but this is hidden within the previously defined functions.
Equations
- trg.mapped_head = List.map (fun (x : FunctionFreeConjunction sig × Nat) => match x with | (h, i) => List.map (trg.apply_to_function_free_atom i) h) trg.rule.head.zipIdx
Instances For
The length of the mapped_head is the same as the length of the rule head. That is, the trigger result indeed has one list of facts for each of the head disjuncts.
Also for each head disjunct, the number of result facts is equal to the number of atoms in the conjunction.
For a fixed head index, we can view the trigger merely as a substitution that internally captures the Skolemization of existential variables. In other words, applying this substitution to the specified head disjunct yields exactly the trigger result for the same disjunct. Viewing the trigger as a substitution can be convenient for theorems and proofs.
Equations
- trg.subs_for_mapped_head i v = trg.apply_to_var_or_const (↑i) (VarOrConst.var v)
Instances For
Applying the subs_for_mapped_head is the same as applying the trigger on VarOrConst.
Applying the subs_for_mapped_head is the same as applying the trigger on FunctionFreeAtom.
Applying the subs_for_mapped_head on the head is exactly the trigger result (mapped_head).
The list of fresh terms are the function terms introduced for the existential variables.
Equations
- trg.fresh_terms_for_head_disjunct i lt = List.map (trg.functional_term_for_var i) (trg.rule.existential_vars_for_head_disjunct i lt)
Instances For
This theorem unfolds some of the internal definitions of fresh_terms_for_head_disjunct.
Fresh terms are always functional.
Constants can never be fresh.
Mappings of frontier variables can never be fresh.
For a given fresh term, we can obtain the existential variable that introduced it.
Equations
- trg.existential_var_for_fresh_term i lt t t_mem = (List.find? (fun (v : sig.V) => decide (trg.functional_term_for_var i v = t)) (trg.rule.existential_vars_for_head_disjunct i lt)).get ⋯
Instances For
Indeed getting the existential variable for a functional term introduced for a variable yields exactly this variable (as expected).
For a fact in the trigger result, we can obtain the head atom that yields the fact.
Equations
Instances For
Applying the trigger on the atom from atom_for_result_fact indeed yields the correct fact.
The atom from atom_for_result_fact occurs in the correct rule head disjunct.
For any term in the result (not just fresh ones), we can obtain the corresponding VarOrConst from the rule.
Equations
- trg.var_or_const_for_result_term i f_mem t_mem = (trg.atom_for_result_fact i f_mem).terms[↑⟨List.idxOf t f.terms, ⋯⟩]
Instances For
Applying the trigger on the VarOrConst from var_or_const_for_result_term indeed yields the correct term.
For a term in a result fact, var_or_const_for_result_term returns a VarOrConst that is in atom_for_result_fact.
For a term in a result fact, var_or_const_for_result_term occurs in the correct head disjunct.
A term occurs in the trigger result for a given head index if and only if one of the following three cases holds. 1. The term is a constant in the head. 2. The term results from mapping a frontier variable in the head. 3. The term is a fresh term of the head.
The constants in the trigger result are a subset of the constants from the mapped frontier terms and the constants that occur directly in the rule head.
The trigger is loaded for a FactSet if its mapped body occurs in the fact set.
Equations
- trg.loaded F = (trg.mapped_body.toSet ⊆ F)
Instances For
Applying a GroundTermMapping that is the id on constants after the trigger substitution and on the fact set preserves loadedness.
A trigger head is satisfied for a FactSet if there exists a substitution that agrees with the trigger substitution on all frontier variable such that the mapping of the head occurs in the fact set. This corresponds to FOL semantics. It is important to note here that a trigger being satisfied in this sense does not necessarily mean that it is obsolete! Obsoleteness might be defined almost arbitrarily and for example in the Skolem chase, a satisfied trigger is often not obsolete. However, for the restricted (aka. standard) chase, obsoleteness is defined via satisfaction.
Equations
- One or more equations did not get rendered due to their size.
Instances For
If the exact trigger result is contained in the fact set, then the trigger is also satisfied.
The trigger is satisfied if it is satisfied for some head. Note that this checks out with FOL semantics since the heads are part of a big disjunction.
Equations
Instances For
We consider two trigger to be equivalent if they share the same rule and their substitutions agree on the frontier variables. This entails that they have the same result.
Equations
Instances For
Trigger equivalence is symmetric.
We consider two trigger to be strongly equivalent if they share the same rule and their substitutions agree not only on the frontier variables but on all body variables.
Equations
Instances For
Strong equivalence is symmetric.
Strong equivalence implies equivalence.
Applying the substitutions of strongly equivalent triggers to a body atom yields the same result. (This is not necessarily true if the triggers are only equivalent.)
Strongly equivalent triggers have the same mapped_body. Again, this is not necessarily true for triggers that are only equivalent.
Applying two equivalent triggers to the same (head) atom yields the same result.
As intended, equivalent triggers have the same result.
Equivalent triggers are satisfied on the same fact sets.