Programming lesson
Mastering Heap Tree Verification with Isabelle: A Step-by-Step Guide for COMP4161 Assignment 3
Learn how to tackle COMP4161 Assignment 3 on heap tree verification using Isabelle and AutoCorres. This guide covers tree-sorted invariants, heap-tree-push proofs, and C verification with real-world analogies from AI and gaming.
Introduction to Heap Tree Verification in Isabelle
Welcome to this comprehensive tutorial on advanced topics in software verification, specifically focused on COMP4161 Assignment 3. This assignment, set in May 2026, challenges you to prove correctness properties of a tree heap (priority queue) using Isabelle2024 and AutoCorres 1.11. Whether you're verifying a functional implementation or a C version, this guide will help you understand the key concepts and proof strategies.
Heap trees are everywhere in modern computing—from AI scheduling algorithms to gaming leaderboards. For instance, an AI chatbot like ChatGPT might use a priority queue to manage conversation contexts, where the highest-priority context is always served first. Similarly, in esports, a tournament bracket can be seen as a heap where the top seed is the root. Understanding how to verify such structures is crucial for building reliable software.
Understanding the Tree Heap Data Structure
A tree heap (or simply a heap) is a binary tree where each node satisfies the heap property: the value at any node is greater than or equal to (according to some ordering) the values in its children. This makes the root the highest-priority element. The heap-tree-sorted predicate in Isabelle captures this invariant:
fun heap-tree-sorted :: (\'a\ \ \'a\ \ bool) \ \'a tree\ \ bool where
heap-tree-sorted ord Empty = True |
heap-tree-sorted ord (Node x left right) =
((\y \ set (tree-elements left). ord x y) \
(\y \ set (tree-elements right). ord x y) \
heap-tree-sorted ord left \ heap-tree-sorted ord right)
Notice that unlike a binary search tree, there is no ordering between left and right children. This property is what makes heap operations efficient—O(log n) for push and pop in a balanced tree.
Functional Implementation: heap-tree-push
The core operation in this assignment is heap-tree-push, which replaces the root value and then pushes it down to restore the heap property. Here's the functional implementation from the provided theory file:
fun heap-tree-push :: (\'a\'a\bool) \ \'a\'a tree\'a tree\ where
heap-tree-push ord x Empty = Empty |
heap-tree-push ord x (Node _ left right) =
(if left \ Empty \ ord (tree-top left) x \ (right = Empty \ ord (tree-top left) (tree-top right))
then Node (tree-top left) (heap-tree-push ord x left) right
else if right \ Empty \ ord (tree-top right) x
then Node (tree-top right) left (heap-tree-push ord x right)
else Node x left right)
This function recursively swaps the new value x with the highest-priority child until the heap property is restored. Proving that heap-tree-push maintains heap-tree-sorted is a key task in Question 1.
Proof Strategy for Question 1: Maintaining Sortedness
To prove that heap-tree-push preserves heap-tree-sorted, you'll need to use induction on the tree structure. The lemma you need to prove is:
lemma heap_tree_push_sorted:
assumes "heap-tree-sorted ord t"
shows "heap-tree-sorted ord (heap-tree-push ord x t)"
Here's a step-by-step approach:
- Case analysis on the tree
t: eitherEmptyorNode. - For
Empty, the result isEmpty, which is trivially sorted. - For
Node, consider the two conditional branches inheap-tree-push. - Use the induction hypothesis on the recursive calls to
heap-tree-pushin the subtrees. - Leverage the assumption that the original tree was sorted to relate the root value to the children.
You may find Isabelle's auto and simp methods helpful, but be prepared to use blast or force for more complex goals. Remember to use find_theorems to explore automatically generated facts like rel_tree.
Connecting to the C Implementation and AutoCorres
Part 2 of the assignment shifts to verifying the C implementation using AutoCorres. The C code (provided in heap.c) implements the same push operation but with explicit pointer manipulation. AutoCorres lifts this to a monadic abstraction, making verification more manageable.
The key types are tree-heap-C (the C struct) and the memory model with th-dom and th-vals. Your task in Q2.1 is to prove that two tree-heap-C values are equal if each of their four fields are equal. This is a straightforward lemma using auto:
lemma tree_heap_c_eqI:
assumes "val_C a = val_C b" and "left_C a = left_C b"
and "right_C a = right_C b" and "parent_C a = parent_C b"
shows "a = b"
by (simp add: tree_heap_c_ext_def)
For Q2.2, you'll need to prove that the C function maintains the heap invariant. The inductive predicate tree-in-C defines which pointers are valid and what tree structure they represent. You'll use Hoare logic (via AutoCorres) to specify pre- and post-conditions.
Practical Tips for Using Isabelle and AutoCorres
Here are some tips to streamline your verification:
- Use sledgehammer wisely: It can suggest proofs, but always verify they terminate. If a proof fails, try breaking the goal into smaller subgoals.
- Explore the environment: Use
find_theoremsto discover facts abouttree-heap-C,th_dom, andth_vals. The automatic definitions are your friends. - Leverage existing lemmas: The theory files likely contain helper lemmas. Don't reinvent the wheel.
- Use the right proof method: For structural induction,
inductis your go-to. For properties of sets,autooften suffices.
Real-World Inspiration: AI and Gaming
Heap trees are not just academic—they power the priority queues in AI systems like recommendation engines and game matchmaking. For example, in a battle royale game, a heap can manage player rankings, where the top player is always at the root. When a player's score changes, a push operation updates their position efficiently. Verifying such operations ensures that the game's leaderboard remains correct.
In the context of AI, consider a chatbot that prioritizes user queries. A heap can ensure urgent queries are handled first. The verification techniques you learn here apply directly to building trustworthy AI systems.
Conclusion
This tutorial covered the essential steps for completing COMP4161 Assignment 3, from understanding the heap tree invariant to proving functional correctness and verifying C code with AutoCorres. By following the proof strategies and tips provided, you'll be well-equipped to tackle the assignment. Remember to use Isabelle's powerful automation tools and the wealth of automatically generated facts. Good luck!