Uses abstract interpretation to automatically infer loop invariants, function preconditions, and postconditions for formal verification. Generates invariants that capture program behavior and support correctness proofs in Dafny, Isabelle, Coq, and other verification systems. Use when adding formal specifications to code, generating verification conditions, inferring contracts for functions, or discovering loop invariants for proofs.
Install with Tessl CLI
npx tessl i github:ArabelaTso/Skills-4-SE --skill abstract-invariant-generator94
Does it follow best practices?
Validation for skill structure
This skill uses abstract interpretation to automatically infer loop invariants, function preconditions, and postconditions. It generates formal specifications that support verification and reasoning about program correctness.
Analyze the code to identify where invariants are needed:
Loop Invariants: For each loop
while condition:
# Need: invariant that holds before/after each iteration
bodyFunction Contracts: For each function
def function(params):
# Need: precondition (what must be true on entry)
body
# Need: postcondition (what is guaranteed on exit)Assertions: For verification points
# Need: invariant that holds at this point
assert propertyUse abstract domains to infer properties:
Interval Analysis: Infer numeric ranges
i = 0
while i < n:
# Inferred: 0 ≤ i < n
i += 1
# Inferred: i = nRelational Analysis: Infer relationships between variables
i = 0
j = 0
while i < n:
# Inferred: i = j
i += 1
j += 1Shape Analysis: Infer data structure properties
while node is not None:
# Inferred: node is in the linked list
node = node.nextFor each loop, generate an invariant that:
Template-Based Generation:
Counter Loop:
i = 0
while i < n:
arr[i] = 0
i += 1Generated Invariant:
invariant 0 ≤ i ≤ n
invariant ∀k. 0 ≤ k < i ⟹ arr[k] = 0Accumulator Loop:
sum = 0
i = 0
while i < len(arr):
sum += arr[i]
i += 1Generated Invariant:
invariant 0 ≤ i ≤ len(arr)
invariant sum = Σ(arr[0..i-1])Search Loop:
i = 0
found = False
while i < len(arr) and not found:
if arr[i] == target:
found = True
else:
i += 1Generated Invariant:
invariant 0 ≤ i ≤ len(arr)
invariant ∀k. 0 ≤ k < i ⟹ arr[k] ≠ target
invariant found ⟹ arr[i] = targetInfer what must be true for the function to work correctly:
Array Access Function:
def get_element(arr, index):
return arr[index]Generated Precondition:
requires 0 ≤ index < len(arr)
requires arr is not NoneDivision Function:
def divide(x, y):
return x / yGenerated Precondition:
requires y ≠ 0Linked List Function:
def get_next(node):
return node.nextGenerated Precondition:
requires node is not NoneInfer what the function guarantees on exit:
Maximum Function:
def find_max(arr):
max_val = arr[0]
for i in range(1, len(arr)):
if arr[i] > max_val:
max_val = arr[i]
return max_valGenerated Postcondition:
ensures result ∈ arr
ensures ∀x ∈ arr. x ≤ resultSorting Function:
def sort(arr):
# ... sorting logic ...
return sorted_arrGenerated Postcondition:
ensures len(result) = len(arr)
ensures ∀i. 0 ≤ i < len(result)-1 ⟹ result[i] ≤ result[i+1]
ensures multiset(result) = multiset(arr)Search Function:
def binary_search(arr, target):
# ... search logic ...
return indexGenerated Postcondition:
ensures result = -1 ∨ (0 ≤ result < len(arr) ∧ arr[result] = target)
ensures result ≠ -1 ⟹ arr[result] = target
ensures result = -1 ⟹ target ∉ arrFormat invariants for the target verification system:
Dafny:
method FindMax(arr: array<int>) returns (max: int)
requires arr.Length > 0
ensures max in arr[..]
ensures forall i :: 0 <= i < arr.Length ==> arr[i] <= max
{
max := arr[0];
var i := 1;
while i < arr.Length
invariant 1 <= i <= arr.Length
invariant max in arr[..i]
invariant forall k :: 0 <= k < i ==> arr[k] <= max
{
if arr[i] > max {
max := arr[i];
}
i := i + 1;
}
}Isabelle/HOL:
lemma find_max_correct:
assumes "length arr > 0"
shows "find_max arr ∈ set arr ∧
(∀x ∈ set arr. x ≤ find_max arr)"Coq:
Lemma find_max_correct : forall (arr : list nat),
length arr > 0 ->
In (find_max arr) arr /\
(forall x, In x arr -> x <= find_max arr).ACSL (for C):
/*@ requires n > 0;
@ requires \valid(arr + (0..n-1));
@ ensures \result >= 0 && \result < n;
@ ensures \forall integer k; 0 <= k < n ==> arr[k] <= arr[\result];
@*/
int find_max(int arr[], int n) {
int max_idx = 0;
/*@ loop invariant 1 <= i <= n;
@ loop invariant 0 <= max_idx < i;
@ loop invariant \forall integer k; 0 <= k < i ==> arr[k] <= arr[max_idx];
@ loop variant n - i;
@*/
for (int i = 1; i < n; i++) {
if (arr[i] > arr[max_idx]) {
max_idx = i;
}
}
return max_idx;
}Input Code (Python):
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = keyAnalysis:
Outer Loop (for i in range(1, len(arr))):
Inner Loop (while j >= 0 and arr[j] > key):
Generated Invariants (Dafny):
method InsertionSort(arr: array<int>)
requires arr.Length >= 0
ensures sorted(arr[..])
ensures multiset(arr[..]) == multiset(old(arr[..]))
modifies arr
{
var i := 1;
while i < arr.Length
invariant 1 <= i <= arr.Length
invariant sorted(arr[..i])
invariant multiset(arr[..]) == multiset(old(arr[..]))
{
var key := arr[i];
var j := i - 1;
while j >= 0 && arr[j] > key
invariant -1 <= j < i
invariant sorted(arr[..j+1])
invariant sorted(arr[j+2..i+1])
invariant forall k :: j+2 <= k <= i ==> arr[k] > key
invariant multiset(arr[..]) == multiset(old(arr[..]))
{
arr[j + 1] := arr[j];
j := j - 1;
}
arr[j + 1] := key;
i := i + 1;
}
}
predicate sorted(s: seq<int>) {
forall i, j :: 0 <= i < j < |s| ==> s[i] <= s[j]
}Explanation:
Outer Loop Invariants:
1 <= i <= arr.Length: Loop counter boundssorted(arr[..i]): First i elements are sortedmultiset(arr[..]) == multiset(old(arr[..])): Permutation preservationInner Loop Invariants:
-1 <= j < i: Loop counter boundssorted(arr[..j+1]): Elements before j+1 remain sortedsorted(arr[j+2..i+1]): Shifted elements remain sortedforall k :: j+2 <= k <= i ==> arr[k] > key: Shifted elements are greater than keymultiset(arr[..]) == multiset(old(arr[..])): Permutation preservationinvariant 0 ≤ i ≤ n
invariant low ≤ mid ≤ highinvariant ∀k. 0 ≤ k < i ⟹ P(arr[k])
invariant sorted(arr[0..i])
invariant arr[i] = max(arr[0..i])invariant i + j = n
invariant i = 2 * j
invariant sum = Σ(arr[0..i-1])invariant acyclic(list)
invariant node ∈ reachable(head)
invariant size(tree) = ninvariant multiset(arr) = multiset(old(arr))
invariant set(arr) = set(old(arr))Sometimes initial invariants are too weak. Strengthen them:
Weak:
invariant 0 ≤ i ≤ nStrengthened:
invariant 0 ≤ i ≤ n
invariant ∀k. 0 ≤ k < i ⟹ processed(arr[k])Technique: Add properties about what has been accomplished so far.
Generate invariants for each level:
for i in range(n):
for j in range(m):
matrix[i][j] = 0Invariants:
Outer loop:
invariant 0 ≤ i ≤ n
invariant ∀r. 0 ≤ r < i ⟹ (∀c. 0 ≤ c < m ⟹ matrix[r][c] = 0)
Inner loop:
invariant 0 ≤ j ≤ m
invariant ∀c. 0 ≤ c < j ⟹ matrix[i][c] = 0Handle all exit paths:
while i < n and not found:
if arr[i] == target:
found = True
i += 1Invariants:
invariant 0 ≤ i ≤ n
invariant found ⟹ arr[i-1] = target
invariant ¬found ⟹ (∀k. 0 ≤ k < i ⟹ arr[k] ≠ target)For detailed invariant generation techniques and patterns:
0f00a4f
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.