Automatically infer loop invariants for code verification and correctness proofs. Use when analyzing loops to identify properties that hold throughout execution, generating assertions for verification, proving loop correctness, or documenting loop behavior. Supports Python, Java, C/C++, and language-agnostic analysis. Generates invariants as code assertions (assert statements). Triggers when users ask to infer invariants, find loop properties, generate loop assertions, prove loop correctness, or verify loop behavior.
94
92%
Does it follow best practices?
Impact
97%
1.12xAverage score across 3 eval scenarios
Passed
No known issues
Analyze loops and automatically infer invariants—properties that remain true throughout loop execution. Generate these as code assertions for verification and correctness proofs.
First, locate and understand the loop to analyze:
Loop types to recognize:
for loops with index variableswhile loops with conditionsdo-while loopsExtract key information:
Understand what the loop does:
Categorize the loop:
Identify patterns:
Generate invariants for each applicable category. See invariant-patterns.md for comprehensive patterns.
Properties about variable ranges:
# Loop: for i in range(n)
assert 0 <= i < n
# Loop: while i < len(arr)
assert 0 <= i <= len(arr)
# Loop: two pointers
while left < right:
assert 0 <= left <= right < len(arr)Properties relating variables:
Sum/Accumulation:
total = 0
for i in range(len(arr)):
assert total == sum(arr[0:i]) # Invariant before update
total += arr[i]
assert total == sum(arr) # Post-conditionMax/Min:
max_val = arr[0]
for i in range(1, len(arr)):
assert max_val == max(arr[0:i])
if arr[i] > max_val:
max_val = arr[i]Product:
product = 1
for i in range(len(arr)):
assert product == arr[0] * arr[1] * ... * arr[i-1]
product *= arr[i]Properties showing termination:
# Decreasing to zero
while n > 0:
assert n > 0 # Still positive
n -= 1
assert n >= 0 # Non-negative after decrement
# Increasing to limit
i = 0
while i < n:
assert i < n # Not yet at limit
i += 1
assert i <= n # At most nProperties about structure integrity:
Sorted sublists:
# Insertion sort
for i in range(1, len(arr)):
assert is_sorted(arr[0:i]) # Prefix is sorted
# ... insert arr[i] into sorted positionPartition property:
# Partitioning around pivot
while left < right:
assert all(arr[j] <= pivot for j in range(0, left))
assert all(arr[j] >= pivot for j in range(right, len(arr)))
# ... move pointersSize invariants:
result = []
for i in range(len(items)):
assert len(result) == i # Processed i items so far
if condition(items[i]):
result.append(items[i])Convert inferred invariants into code assertions:
def find_maximum(arr):
"""Find maximum element in array."""
assert len(arr) > 0, "Array must not be empty" # Pre-condition
max_val = arr[0]
for i in range(1, len(arr)):
# Loop invariants
assert 0 < i < len(arr), "Index in valid range"
assert max_val == max(arr[0:i]), "max_val is maximum so far"
assert max_val in arr[0:i], "max_val is from processed elements"
if arr[i] > max_val:
max_val = arr[i]
assert max_val == max(arr), "max_val is maximum of entire array" # Post-condition
return max_valpublic int findMaximum(int[] arr) {
assert arr.length > 0 : "Array must not be empty";
int maxVal = arr[0];
for (int i = 1; i < arr.length; i++) {
assert i > 0 && i < arr.length : "Index in valid range";
assert maxVal == max(arr, 0, i) : "maxVal is maximum so far";
if (arr[i] > maxVal) {
maxVal = arr[i];
}
}
assert maxVal == max(arr, 0, arr.length) : "maxVal is maximum";
return maxVal;
}int find_maximum(int arr[], int n) {
assert(n > 0); // Pre-condition
int max_val = arr[0];
for (int i = 1; i < n; i++) {
assert(i >= 1 && i < n); // Bounds
assert(max_val >= arr[0]); // max_val is at least first element
// Note: Can't easily express "max of subarray" in C without helper
if (arr[i] > max_val) {
max_val = arr[i];
}
}
return max_val;
}Check that inferred invariants are correct:
Invariant must be true before the loop starts:
# Loop: total = 0; for i in range(n): total += arr[i]
# Invariant: total == sum(arr[0:i])
# Check: Before loop, i=0, total=0, sum(arr[0:0])=0 ✓Invariant remains true after each iteration:
# Assume invariant true at start of iteration i
# Show it's true at start of iteration i+1
# Before: total == sum(arr[0:i])
# Execute: total += arr[i]
# After: total == sum(arr[0:i]) + arr[i] == sum(arr[0:i+1]) ✓Invariant + termination condition proves post-condition:
# After loop: i == n (termination) and total == sum(arr[0:i]) (invariant)
# Therefore: total == sum(arr[0:n]) == sum(arr) ✓Generate invariants for each nesting level:
# Matrix sum
total = 0
for i in range(rows):
assert total == sum(matrix[0:i, :]) # Outer invariant
for j in range(cols):
assert total == sum(matrix[0:i, :]) + sum(matrix[i, 0:j]) # Inner
total += matrix[i][j]Track relationships between variables:
# Two pointers
left, right = 0, len(arr) - 1
while left < right:
assert 0 <= left <= right < len(arr)
assert left + right == len(arr) - 1 # Relationship invariant
# (only true if they move symmetrically)
left += 1
right -= 1Handle break/return statements:
# Search with early exit
found = False
for i in range(len(arr)):
assert not found or arr[i-1] == target # If found, previous element was it
if arr[i] == target:
found = True
break
assert not found or target in arr[0:i+1] # Post-conditionTrack effects on external state:
# Modifying a list
for i in range(len(arr)):
assert len(arr) remains constant # Size doesn't change
assert arr[0:i] are processed correctly
arr[i] = transform(arr[i])User request:
"Infer invariants for this sum loop"
def array_sum(arr):
total = 0
for i in range(len(arr)):
total += arr[i]
return totalResponse:
def array_sum(arr):
assert len(arr) >= 0 # Pre-condition: valid array
total = 0
for i in range(len(arr)):
# Loop invariants
assert 0 <= i < len(arr), "Index in valid range"
assert total == sum(arr[0:i]), "total is sum of elements so far"
total += arr[i]
assert total == sum(arr), "total is sum of entire array" # Post-condition
return totalExplanation:
0 <= i < len(arr)total == sum(arr[0:i])i increases each iterationUser request:
"What are the invariants for binary search?"
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1Response:
def binary_search(arr, target):
assert is_sorted(arr), "Array must be sorted" # Pre-condition
left, right = 0, len(arr) - 1
while left <= right:
# Loop invariants
assert 0 <= left <= len(arr), "left in valid range"
assert -1 <= right < len(arr), "right in valid range"
assert left <= right + 1, "Valid search range"
# Critical invariant: if target exists, it's in [left, right]
assert (target not in arr or
any(arr[i] == target for i in range(left, right + 1))), \
"Target in current range if it exists"
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
assert arr[mid] < target, "All elements left of mid < target"
left = mid + 1
else:
assert arr[mid] > target, "All elements right of mid > target"
right = mid - 1
assert left > right, "Search range exhausted"
assert target not in arr, "Target not found implies not in array"
return -1User request:
"Generate invariants for insertion sort"
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] = keyResponse:
def insertion_sort(arr):
for i in range(1, len(arr)):
# Outer loop invariant
assert is_sorted(arr[0:i]), "arr[0:i] is sorted"
assert set(arr) == set(original_arr), "Elements unchanged (only reordered)"
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
# Inner loop invariants
assert -1 <= j < i, "j in valid range"
assert arr[j] > key, "Current element larger than key"
assert is_sorted(arr[0:j]), "Left part still sorted"
assert arr[j+2:i+1] are shifted right and sorted
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
assert is_sorted(arr[0:i+1]), "arr[0:i+1] is now sorted"
assert is_sorted(arr), "Entire array is sorted"Start with obvious properties:
Look for accumulation patterns:
Identify preservation properties:
Think about the loop's purpose:
Verify your invariants:
Be specific:
i >= 00 <= i < len(arr)0 <= i < len(arr) and sum_val == sum(arr[0:i])Use helper predicates for clarity:
def is_sorted(arr):
return all(arr[i] <= arr[i+1] for i in range(len(arr)-1))
def is_partition(arr, pivot, left, right):
return (all(arr[i] <= pivot for i in range(left)) and
all(arr[i] >= pivot for i in range(right, len(arr))))For comprehensive invariant patterns across different loop types and languages, see invariant-patterns.md.
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.