-
Notifications
You must be signed in to change notification settings - Fork 95
feat: add 12 CS algorithm implementations with correctness proofs #383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
brando90
wants to merge
12
commits into
leanprover:main
Choose a base branch
from
brando90:feat/graph-dfs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
5bf6923
feat(Algorithms): add DFS graph reachability with soundness and compl…
brando90 ea12cf9
feat(Algorithms): add BFS shortest-path with soundness proof
brando90 aefb2f8
feat(Algorithms): add binary search with found-element soundness proof
brando90 d34ed69
feat(Algorithms): add insertion sort with sorted and permutation proofs
brando90 fff016e
feat(Algorithms): add bubble sort with permutation and fixed-point pr…
brando90 cc63d55
feat(Algorithms): add selection sort with full correctness proofs
brando90 de74fca
feat(Algorithms): add quick sort with full correctness proofs
brando90 06b1506
feat(Algorithms): add counting sort with full correctness proofs
brando90 bf718dd
feat(Algorithms): add longest common subsequence with correctness proofs
brando90 08d6c8e
feat(Algorithms): add edit distance with correctness proofs
brando90 3eaf499
feat: add Dijkstra's shortest path algorithm with full correctness pr…
brando90 d59a02f
feat: add heap sort with permutation and length preservation proofs
brando90 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| /- | ||
| Copyright (c) 2025 Brando Miranda. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: Brando Miranda | ||
| -/ | ||
|
|
||
| module | ||
|
|
||
| public import Cslib.Init | ||
|
|
||
| @[expose] public section | ||
|
|
||
| /-! | ||
| # Edit Distance | ||
|
|
||
| This file implements the edit distance (Levenshtein distance) algorithm on lists of natural | ||
| numbers and proves key correctness properties. | ||
|
|
||
| ## Main definitions | ||
|
|
||
| - `editDist`: Fuel-based edit distance computation. | ||
| - `editDistance`: Top-level edit distance. | ||
|
|
||
| ## Main results | ||
|
|
||
| - `editDistance_self`: Edit distance from a list to itself is 0. | ||
| - `editDistance_nil_left`: Edit distance from empty list equals target length. | ||
| - `editDistance_nil_right`: Edit distance to empty list equals source length. | ||
| - `editDistance_le_sum`: Edit distance is bounded by the sum of input lengths. | ||
|
|
||
| ## References | ||
|
|
||
| Adapted from the VeriBench benchmark (https://github.com/brandomiranda/veribench). | ||
| -/ | ||
|
|
||
| set_option autoImplicit false | ||
|
|
||
| namespace Cslib.Algorithms.DynamicProgramming.EditDistance | ||
|
|
||
| /-- Fuel-based edit distance (Levenshtein distance). -/ | ||
| def editDist : List Nat → List Nat → Nat → Nat | ||
| | [], ys, _ => ys.length | ||
| | xs, [], _ => xs.length | ||
| | _, _, 0 => 0 | ||
| | x :: xs, y :: ys, fuel + 1 => | ||
| if x = y then editDist xs ys fuel | ||
| else 1 + Nat.min (Nat.min | ||
| (editDist xs (y :: ys) fuel) | ||
| (editDist (x :: xs) ys fuel)) | ||
| (editDist xs ys fuel) | ||
|
|
||
| /-- Top-level edit distance. Uses fuel = sum of input lengths. -/ | ||
| def editDistance (s1 s2 : List Nat) : Nat := | ||
| editDist s1 s2 (s1.length + s2.length) | ||
|
|
||
| /-! ## Tests -/ | ||
|
|
||
| example : editDistance [1, 2, 3] [2, 2, 3] = 1 := by decide | ||
| example : editDistance [] ([] : List Nat) = 0 := by decide | ||
| example : editDistance [1, 2, 3] [1, 2, 3] = 0 := by decide | ||
| example : editDistance [1, 2, 3] [] = 3 := by decide | ||
| example : editDistance [] [1, 2, 3] = 3 := by decide | ||
| example : editDistance [1, 2, 3] [1, 2, 3, 4] = 1 := by decide | ||
| example : editDistance [1, 2, 3] [4, 5, 6] = 3 := by decide | ||
|
|
||
| /-! ## Reflexivity -/ | ||
|
|
||
| /-- Edit distance from a list to itself is 0. -/ | ||
| theorem editDist_self (l : List Nat) (fuel : Nat) (hfuel : fuel ≥ l.length + l.length) : | ||
| editDist l l fuel = 0 := by | ||
| induction fuel generalizing l with | ||
| | zero => | ||
| match l with | ||
| | [] => rfl | ||
| | _ :: _ => exfalso; simp only [List.length_cons] at hfuel; omega | ||
| | succ n ih => | ||
| match l with | ||
| | [] => rfl | ||
| | x :: xs => | ||
| simp only [editDist, ite_true] | ||
| exact ih xs (by simp only [List.length_cons] at hfuel; omega) | ||
|
|
||
| /-- Top-level: edit distance from a list to itself is 0. -/ | ||
| theorem editDistance_self (l : List Nat) : editDistance l l = 0 := | ||
| editDist_self l (l.length + l.length) (Nat.le_refl _) | ||
|
|
||
| /-! ## Empty list properties -/ | ||
|
|
||
| /-- Edit distance from empty list equals target length. -/ | ||
| theorem editDistance_nil_left (l : List Nat) : editDistance [] l = l.length := by | ||
| simp [editDistance, editDist] | ||
|
|
||
| /-- Edit distance to empty list equals source length. -/ | ||
| theorem editDistance_nil_right (l : List Nat) : editDistance l [] = l.length := by | ||
| cases l <;> simp [editDistance, editDist] | ||
|
|
||
| /-! ## Upper bound -/ | ||
|
|
||
| /-- Edit distance is bounded by the sum of input lengths. -/ | ||
| theorem editDist_le_sum (l1 l2 : List Nat) (fuel : Nat) | ||
| (hfuel : fuel ≥ l1.length + l2.length) : | ||
| editDist l1 l2 fuel ≤ l1.length + l2.length := by | ||
| induction fuel generalizing l1 l2 with | ||
| | zero => | ||
| match l1, l2 with | ||
| | [], [] => simp [editDist] | ||
| | [], _ :: _ => simp only [editDist]; omega | ||
| | _ :: _, _ => exfalso; simp only [List.length_cons] at hfuel; omega | ||
| | succ n ih => | ||
| match l1, l2 with | ||
| | [], _ => simp only [editDist]; omega | ||
| | _ :: _, [] => simp only [editDist, List.length_cons]; omega | ||
| | x :: xs, y :: ys => | ||
| simp only [editDist] | ||
| split | ||
| · -- x = y | ||
| have := ih xs ys (by simp only [List.length_cons] at hfuel ⊢; omega) | ||
| simp only [List.length_cons]; omega | ||
| · -- x ≠ y: 1 + min3(del, ins, sub) ≤ l1.length + l2.length | ||
| have h1 := ih xs (y :: ys) (by simp only [List.length_cons] at hfuel ⊢; omega) | ||
| simp only [List.length_cons] at h1 ⊢ | ||
| have hmin : ((editDist xs (y :: ys) n).min (editDist (x :: xs) ys n)).min | ||
| (editDist xs ys n) ≤ editDist xs (y :: ys) n := | ||
| Nat.le_trans (Nat.min_le_left _ _) (Nat.min_le_left _ _) | ||
| omega | ||
|
|
||
| /-- Top-level: edit distance is bounded by the sum of input lengths. -/ | ||
| theorem editDistance_le_sum (l1 l2 : List Nat) : | ||
| editDistance l1 l2 ≤ l1.length + l2.length := | ||
| editDist_le_sum l1 l2 (l1.length + l2.length) (Nat.le_refl _) | ||
|
|
||
| end Cslib.Algorithms.DynamicProgramming.EditDistance | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| /- | ||
| Copyright (c) 2025 Brando Miranda. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: Brando Miranda | ||
| -/ | ||
|
|
||
| module | ||
|
|
||
| public import Cslib.Init | ||
|
|
||
| @[expose] public section | ||
|
|
||
| /-! | ||
| # Longest Common Subsequence | ||
|
|
||
| This file implements the longest common subsequence (LCS) algorithm on lists of natural numbers | ||
| and proves key correctness properties. | ||
|
|
||
| ## Main definitions | ||
|
|
||
| - `lcs`: Fuel-based LCS computation. | ||
| - `longestCommonSubsequence`: Top-level LCS. | ||
|
|
||
| ## Main results | ||
|
|
||
| - `lcs_sublist_left`: The LCS is a sublist of the first input. | ||
| - `lcs_sublist_right`: The LCS is a sublist of the second input. | ||
| - `lcs_length_le_left`: The LCS length is bounded by the first input length. | ||
| - `lcs_length_le_right`: The LCS length is bounded by the second input length. | ||
| - `lcs_self`: The LCS of a list with itself is the list. | ||
|
|
||
| ## References | ||
|
|
||
| Adapted from the VeriBench benchmark (https://github.com/brandomiranda/veribench). | ||
| -/ | ||
|
|
||
| set_option autoImplicit false | ||
|
|
||
| namespace Cslib.Algorithms.DynamicProgramming.LCS | ||
|
|
||
| /-- Fuel-based longest common subsequence. -/ | ||
| def lcs : List Nat → List Nat → Nat → List Nat | ||
| | _, _, 0 => [] | ||
| | [], _, _ => [] | ||
| | _, [], _ => [] | ||
| | x :: xs, y :: ys, fuel + 1 => | ||
| if x = y then x :: lcs xs ys fuel | ||
| else | ||
| let r1 := lcs (x :: xs) ys fuel | ||
| let r2 := lcs xs (y :: ys) fuel | ||
| if r1.length ≥ r2.length then r1 else r2 | ||
|
|
||
| /-- Top-level LCS. Uses fuel = sum of input lengths. -/ | ||
| def longestCommonSubsequence (l1 l2 : List Nat) : List Nat := | ||
| lcs l1 l2 (l1.length + l2.length) | ||
|
|
||
| /-! ## Tests -/ | ||
|
|
||
| example : longestCommonSubsequence [1, 2, 3, 4] [1, 3, 5] = [1, 3] := by decide | ||
| example : longestCommonSubsequence [] [1, 2, 3] = ([] : List Nat) := by decide | ||
| example : longestCommonSubsequence [1, 2, 3] [] = ([] : List Nat) := by decide | ||
| example : longestCommonSubsequence [1, 2, 3] [1, 2, 3] = [1, 2, 3] := by decide | ||
| example : longestCommonSubsequence [1, 2, 3, 4, 5] [2, 4, 6] = [2, 4] := by decide | ||
| example : longestCommonSubsequence [3, 5, 7, 9] [1, 3, 6, 7, 8] = [3, 7] := by decide | ||
|
|
||
| /-! ## Sublist proofs -/ | ||
|
|
||
| /-- `lcs l1 l2 fuel` is a sublist of `l1` when fuel ≥ l1.length + l2.length. -/ | ||
| theorem lcs_sublist_left (l1 l2 : List Nat) (fuel : Nat) | ||
| (hfuel : fuel ≥ l1.length + l2.length) : | ||
| (lcs l1 l2 fuel).Sublist l1 := by | ||
| induction fuel generalizing l1 l2 with | ||
| | zero => | ||
| match l1 with | ||
| | [] => exact List.Sublist.slnil | ||
| | _ :: _ => exfalso; simp only [List.length_cons] at hfuel; omega | ||
| | succ n ih => | ||
| match l1, l2 with | ||
| | [], _ => exact List.Sublist.slnil | ||
| | _ :: _, [] => exact List.nil_sublist _ | ||
| | x :: xs, y :: ys => | ||
| simp only [lcs] | ||
| split | ||
| · exact (ih xs ys (by simp only [List.length_cons] at hfuel ⊢; omega)).cons₂ _ | ||
| · split | ||
| · exact ih (x :: xs) ys (by simp only [List.length_cons] at hfuel ⊢; omega) | ||
| · exact (ih xs (y :: ys) | ||
| (by simp only [List.length_cons] at hfuel ⊢; omega)).cons _ | ||
|
|
||
| /-- `lcs l1 l2 fuel` is a sublist of `l2` when fuel ≥ l1.length + l2.length. -/ | ||
| theorem lcs_sublist_right (l1 l2 : List Nat) (fuel : Nat) | ||
| (hfuel : fuel ≥ l1.length + l2.length) : | ||
| (lcs l1 l2 fuel).Sublist l2 := by | ||
| induction fuel generalizing l1 l2 with | ||
| | zero => | ||
| match l2 with | ||
| | [] => exact List.Sublist.slnil | ||
| | _ :: _ => | ||
| match l1 with | ||
| | [] => exact List.nil_sublist _ | ||
| | _ :: _ => exfalso; simp only [List.length_cons] at hfuel; omega | ||
| | succ n ih => | ||
| match l1, l2 with | ||
| | [], _ => exact List.nil_sublist _ | ||
| | _ :: _, [] => exact List.Sublist.slnil | ||
| | x :: xs, y :: ys => | ||
| simp only [lcs] | ||
| split | ||
| · rename_i h_eq; rw [h_eq] | ||
| exact (ih xs ys (by simp only [List.length_cons] at hfuel ⊢; omega)).cons₂ _ | ||
| · split | ||
| · exact (ih (x :: xs) ys | ||
| (by simp only [List.length_cons] at hfuel ⊢; omega)).cons _ | ||
| · exact ih xs (y :: ys) (by simp only [List.length_cons] at hfuel ⊢; omega) | ||
|
|
||
| /-! ## Length bounds -/ | ||
|
|
||
| /-- LCS length is bounded by the first input length. -/ | ||
| theorem lcs_length_le_left (l1 l2 : List Nat) (fuel : Nat) | ||
| (hfuel : fuel ≥ l1.length + l2.length) : | ||
| (lcs l1 l2 fuel).length ≤ l1.length := | ||
| (lcs_sublist_left l1 l2 fuel hfuel).length_le | ||
|
|
||
| /-- LCS length is bounded by the second input length. -/ | ||
| theorem lcs_length_le_right (l1 l2 : List Nat) (fuel : Nat) | ||
| (hfuel : fuel ≥ l1.length + l2.length) : | ||
| (lcs l1 l2 fuel).length ≤ l2.length := | ||
| (lcs_sublist_right l1 l2 fuel hfuel).length_le | ||
|
|
||
| /-! ## Self-LCS -/ | ||
|
|
||
| /-- The LCS of a list with itself is the list itself. -/ | ||
| theorem lcs_self (l : List Nat) (fuel : Nat) (hfuel : fuel ≥ l.length + l.length) : | ||
| lcs l l fuel = l := by | ||
| induction fuel generalizing l with | ||
| | zero => | ||
| match l with | ||
| | [] => rfl | ||
| | _ :: _ => exfalso; simp only [List.length_cons] at hfuel; omega | ||
| | succ n ih => | ||
| match l with | ||
| | [] => rfl | ||
| | x :: xs => | ||
| simp only [lcs, ite_true] | ||
| congr 1 | ||
| exact ih xs (by simp only [List.length_cons] at hfuel; omega) | ||
|
|
||
| /-- Top-level: LCS of a list with itself. -/ | ||
| theorem longestCommonSubsequence_self (l : List Nat) : | ||
| longestCommonSubsequence l l = l := | ||
| lcs_self l (l.length + l.length) (Nat.le_refl _) | ||
|
|
||
| end Cslib.Algorithms.DynamicProgramming.LCS |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several major points with this PR, hence offering the review here: