Tree Derivation #
The TreeDerivation is the tree version of the ChaseDerivation.
Since we allow rules to feature disjunctions,
there are multiple possible results for a given trigger. The ChaseDerivation picks one possible choice.
For the TreeDerivation, we consider all possiblities at once. That is, the tree branches out for the disjunctions.
We try to mimic much of the machinery introduced for ChaseDerivation but we will see that some of this requires a different approach.
Most prominently, we now need to consider addresses of nodes in the tree to be able to define a proper predecessor relation.
Chase Step #
To define how one chase step follows from the previous, we introduce two auxiliary definitions that capture two cases that can occur for a given ChaseNode in a TreeDerivation. This is similar to the definitions for ChaseDerivation only that now the result is a list of ChaseNodes.
Case (
exists_trigger_list): There still exists anactivetrigger, in which case one chase node for each disjunct of the trigger is introduced and contains the facts from the previous step together with the respective head result of the trigger.Case (
not_exists_trigger_list): There is no active trigger. Then the successor list must be empty, i.e. the respective tree node does not have any children.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
Instances For
Equations
Instances For
The TreeDerivation Structure #
The backbone of the TreeDerivation is a FiniteDegreeTree of ChaseNodes with a couple of conditions.
- We enforce that there is at least an initial
ChaseNode. - At each step in the derivation, either there exists a trigger that yields the child nodes or there is no trigger and consequently the derivation stops at this point. This is expressed by the two auxiliary definitions above.
- No triggers are active on leaf nodes.
- For each trigger, there exists a depth in the tree from which on the trigger is never active anymore.
Conditions 3 and 4 together are "fairness", i.e. each trigger must eventually be non-active. Fairness ensures that the chase result (or in this case each fact set in the chase result) is indeed a model.
- tree : FiniteDegreeTree (ChaseNode obs rules)
- triggers_exist (ns : List Nat) (before : ChaseNode obs rules) : before ∈ (self.tree.drop ns).root → let after := (self.tree.drop ns).childNodes; exists_trigger_list obs rules before after ∨ not_exists_trigger_list obs rules before after
Instances For
Basic Definitions #
Here we introduce some auxiliary definitions and theorems and we lift some of the machinery of the underlying FiniteDegreeTree to TreeDerivation.
We can convert a TreeDerivation directly to a FiniteDegreeTreeWithRoot.
Equations
- td.toFiniteDegreeTreeWithRoot = ⟨td.tree, ⋯⟩
Instances For
Membership of ChaseNodes in the TreeDerivation directly corresponds to membership in the FiniteDegreeTree.
Equations
- TreeDerivation.instMembershipChaseNode = { mem := fun (td : TreeDerivation obs rules) (node : ChaseNode obs rules) => node ∈ td.tree }
Each subtree of the underlying FiniteDegreeTree is itself a TreeDerivation as long as its root is not none.
Equations
- td.derivation_for_suffix t2 suffix t2_root_some = { tree := t2, isSome_root := t2_root_some, triggers_exist := ⋯, fairness_leaves := ⋯, fairness_infinite_branches := ⋯ }
Instances For
The root of the TreeDerivation is the initial ChaseNode. We know that this is never none.
Instances For
The root is a member.
The (immediate) ChildNodes #
For a TreeDerivation derivation, its childNodes are the ChaseNodes immediately following the root.
We mainly introduce a couple of theorems here that abstract away the triggers_exist condition from the TreeDerivation definition.
Equations
- td.childNodes = td.tree.childNodes
Instances For
Each child node is a member.
The origin of the childNodes needs to be set.
The trigger used to derive the childNodes is active for head.
The childNodes are not nil if and only if some trigger is active on root.
The fact set of each of the childNodes consists exactly of the facts from root and the result of the trigger that introduces the child node.
TreeDerivation Subtrees #
We define a suffix/subtree relation on TreeDerivation simply as the subtree relation of the underlying FiniteDegreeTree.
Instances For
Equations
- TreeDerivation.«term_<:+_» = Lean.ParserDescr.trailingNode `TreeDerivation.«term_<:+_» 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " <:+ ") (Lean.ParserDescr.cat `term 51))
Instances For
Members of our subtrees are also our members.
Child Trees #
We can drop the root of a TreeDerivation and obtain a (possibly empty) list of childTrees, which are again TreeDerivations.
We obtain the child trees of the FiniteDegreeTree and convert each of them into a TreeDerivation using derivation_for_suffix. We know that all of those trees have non-none roots.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Membership in childTrees can be boiled down to membership in FiniteDegreeTree.childTrees.
childNodes can be expressed by mapping each childTrees to its root.
Each childTrees is a suffix.
A derivation is a subtree of another if and only if both are the same or the first is a suffix of one of the second's child trees.
Induction Principle for Members #
Similar to FiniteDegreeTree.mem_rec, we define an induction principle to show
properties of ChaseNodes in a TreeDerivation.
Looking ahead a bit, we will also want to define a predecessor relation on nodes in the tree as we did for nodes in a ChaseDerivation.
This is not so elegant here though as the same node might indeed occur multiple times in the tree.
Therefore, we are going to associate each node with its address in the tree to tell them apart.
Since we are doing this anyway, it makes sense to define the induction principle already with respect to this kind of node.
NodeWithAddress #
The NodeWithAddress is a structure of a ChaseNode, an address (i.e. a List Nat), and a proof that the chase node is indeed at the given address.
We also introduce a couple of covenience functions and theorems for the NodeWithAddress.
- node : ChaseNode obs rules
Instances For
Each NodeWithAddress induces a subderivation in the TreeDerivation.
Equations
- node.subderivation = td.derivation_for_suffix (td.tree.drop node.address) ⋯ ⋯
Instances For
We can cast a node for one of our subderivations into our own node.
Equations
Instances For
The NodeWithAddress version of TreeDerivation.root.
Instances For
The NodeWithAddress version of TreeDerivation.childNodes.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The NodeWithAddress is a member.
Two NodeWithAddress are equal if their addresses are.
subderivation is indeed a subtree.
The root of the subderivation is the node itself with empty address.
The root of the subderivation is the node itself.
NodeWithAddress.childNodes has the same length as TreeDerivation.childNodes.
NodeWithAddress.childNodes and TreeDerivation.childNodes are (almost) equal.
Membership for NodeWithAddress.childNodes and TreeDerivation.childNodes is (almost) the same.
Getting specific elements from child nodes can be translated between NodeWithAddress.childNodes and TreeDerivation.childNodes.
The subderivation for a child node is a child tree.
Now we are ready for the actual induction principle on NodeWithAddresss in a TreeDerivation.
If we want to show a motive for all nodes in a derivation, it is enough to show the motive for the root and for each arbitrary child node in each abitrary subderivation where the motive in turn already holds for the root. This can be used with the induction tactic.
A node is a member of some element of childTrees if and only if there is a subderivation where the node occurs in the childNodes. Part of this proof uses the induction principle defined above.
Subset Monotonicity of Facts in ChaseNodes #
Since ChaseNodes always extend the previous facts, the fact sets can only be growing along the branches of the TreeDerivation.
This has a couple of convenient implications. For example, the root of a TreeDerivation can never occur in its childTrees.
Each member's facts contain the root facts.
The root cannot occur in the childTrees. Otherwise, it would be introduced using a trigger but then this trigger is already obsolete since all the facts from root already occur in the very beginning. We use ObsoletenessCondition.contains_trg_result_implies_cond here.
By root_not_mem_childTrees, if we have a subtree but our root occurs in the subtree, then our subtree is equal to us.
Predecessor Relation #
Opposed to the ChaseDerivation, we define the predecessor relation direclty using addresses here.
This is because ChaseNodes may indeed occur multiple times in a TreeDerivation (just not in the same branch)
and therefore the approach used in ChaseDerivation would not quite work.
In particular, note that the TreeDerivation has no equivalent for ChaseDerivation.suffix_of_suffix_of_suffix_of_head_mem.
Also, even with the address approach, the relation is not total here (which is expected for a tree).
Node $n$ is a predecessor of node $m$ if the address of $n$ is a prefix of the address of $m$. Predecessor can therefore also be understood as ancestor in the tree.
Instances For
Equations
- TreeDerivation.«term_≼_» = Lean.ParserDescr.trailingNode `TreeDerivation.«term_≼_» 50 50 (Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol " ≼ ") (Lean.ParserDescr.cat `term 51))
Instances For
The predecessor relation is stable across suffixes. That is, predecessor in our suffix are also predecessor for us. We only need to cast the nodes.
The predecessor relation is reflexive.
The predecessor relation is antisymmetric.
The predecessor relation is transitive.
The root is a predecessor of each childNodes.
The facts of our predecessor are a subset of our facts.
Branches and Chase Result #
Here, we define the result of a TreeDerivation, which requires us to also define the branches of the TreeDerivation. It should be no surprise that these are ChaseDerivations. The result is then just the set of the results of all the ChaseDerivations in the tree. We already know from the ChaseDerivation that each element of TreeDerivation.result is therefore a model of the rules.
Each branch of the underlying tree can be transformed into a proper ChaseDerivation.
Equations
- td.derivation_for_branch branch branch_mem = { branch := branch, isSome_head := ⋯, triggers_exist := ⋯, fairness := ⋯ }
Instances For
The branches of the TreeDerivation are defined as all the ChaseDerivation that occur as branches in the tree.
Instances For
Each ChaseDerivation constructed using derivation_for_branch occurs in branches.
The result is the set of FactSets that correspond to the results of the branches.
Equations
- td.result = td.branches.map ChaseDerivation.result
Instances For
Each element of the result models the rules.
Terms in the Chase #
We make some general observations about certain terms that might occur in the chase.
- Constants can only originate directly from rules or from the initial fact set. No other constants can be introduced.
- Functional terms can either also originate from the initial fact set or they are introduced as fresh terms by a trigger.
The second observation entails that the precense of a functional term that does not occur in the initial fact set implies that the trigger that introduces this term must have been applied in some node.
Constants in the chase can only come from the initial fact set or from a constant in a rule.
Each functional term in the chase originates as a fresh term from a trigger if it was not already part of the initial fact set.
If a functional term occurs in the chase, then the trigger that introduces this term must have been used in the chase, unless the term already occurs in the initial fact set.
If a functional term occurs in the chase, then the result of the trigger that introduces this term is contained in the current node, unless the functional term already occurs in the initial fact set.