Zulip Chat Archive

Stream: general

Topic: golf with `grind`


Asei Inoue (Nov 20 2025 at 13:50):

import Std.Tactic.Do
import Batteries

open Std

variable {α : Type}
variable [LT α] [DecidableLT α]

@[grind =]
def ICan'tBelieveItCanSort (arr : Array α) := Id.run do
  let N := arr.size
  let mut vec := arr.toVector
  for hi : i in [:N] do
    for hj : j in [:N] do
      if vec[i] < vec[j] then
        vec := vec.swap i j
  return vec.toArray

open Std.Do

set_option mvcgen.warning false

theorem ICan'tBelieveItCanSort_perm (arr : Array α) : Array.Perm (ICan'tBelieveItCanSort arr) arr := by
  generalize h : ICan'tBelieveItCanSort arr = x
  apply Id.of_wp_run_eq h
  mvcgen invariants
  · ⇓⟨_cursor, vec => Array.Perm arr vec.toArray
  · ⇓⟨_cursor, vec => Array.Perm arr vec.toArray
  with grind [Array.Perm.trans, Array.Perm.symm, Array.swap_perm]

@[simp, grind =]
theorem Vector.toArray_extract_size {α : Type} {n : Nat} (v : Vector α n) :
    v.toArray.extract 0 n = v.toArray := by
  grind

variable [LE α] [IsLinearOrder α] [LawfulOrderLT α]

@[grind! =>]
theorem cursor_pos_le_length (n : Nat) (xs : [0:n].toList.Cursor) : xs.pos  n := by
  have : xs.prefix.length + xs.suffix.length = n := by
    simp [ List.length_append, xs.property]
  grind only

theorem ICan'tBelieveItCanSort_sorted (arr : Array α) : ICan'tBelieveItCanSort arr |>.Pairwise (·  ·) := by
  generalize h : ICan'tBelieveItCanSort arr = x
  apply Id.of_wp_run_eq h
  mvcgen invariants
  · ⇓⟨cursor, vec => vec.take cursor.pos |>.toArray.Pairwise (·  ·)
  · ⇓⟨cursor, vec => by
    expose_names
    exact (vec.take cur |>.toArray.Pairwise (·  ·)) 
       i (_ : i < cursor.pos), vec[i]'(by grind)  vec[cur]'(by grind)
  with grind

  case vc1 =>
    simp [Array.pairwise_iff_getElem] at *
    grind

  case vc4 =>
    simp [Array.pairwise_iff_getElem] at *
    grind

Could we make the proof finish directly at the with grind part by adding the right grind lemmas?

Asei Inoue (Nov 20 2025 at 13:52):

(this code is based on one given by Anthony Wang, thanks )

Asei Inoue (Nov 20 2025 at 13:54):

Background

I’m interested in verifying algorithms with mvcgen. After trying various examples, it seems that proofs involving index accesses become difficult for grind to handle. I’m curious whether adding appropriate grind lemmas could eliminate that difficulty.

Asei Inoue (Nov 23 2025 at 11:59):

succeed!

import Std.Tactic.Do
import Batteries.Data.Array

open Std

variable {α : Type}
variable [LT α] [DecidableLT α]

@[grind =]
def ICan'tBelieveItCanSort (arr : Array α) := Id.run do
  let n := arr.size
  let mut vec := arr.toVector
  for hi : i in [0:n] do
    for hj : j in [0:n] do
      if vec[i] < vec[j] then
        vec := vec.swap i j
  return vec.toArray

open Std.Do

set_option mvcgen.warning false

theorem ICan'tBelieveItCanSort_perm (arr : Array α) : Array.Perm (ICan'tBelieveItCanSort arr) arr := by
  generalize h : ICan'tBelieveItCanSort arr = x
  apply Id.of_wp_run_eq h
  mvcgen invariants
  · ⇓⟨_cursor, vec => Array.Perm arr vec.toArray
  · ⇓⟨_cursor, vec => Array.Perm arr vec.toArray
  with grind [Array.Perm.trans, Array.Perm.symm, Array.swap_perm]

@[grind =]
theorem Vector.toArray_extract_size {α : Type} {n : Nat} (v : Vector α n) :
    v.toArray.extract 0 n = v.toArray := by
  grind

@[grind! =>]
theorem cursor_pos_le_length (n : Nat) (xs : [0:n].toList.Cursor) : xs.pos  n := by
  have : xs.prefix.length + xs.suffix.length = n := by
    simp [ List.length_append, xs.property]
  grind only

theorem Array.pairwise_take_swap {α : Type} [LE α] [IsPartialOrder α] {arr : Array α}
  (s t : Nat) (hs : s < arr.size) (ht : t < arr.size)
  (h : Array.Pairwise (·  ·) (arr.take s))
  (le1 : arr[s]  arr[t])
  (le2 :  (i : Nat) (_ : i < t), arr[i]  arr[s])
    : Array.Pairwise (·  ·) ((arr.swap s t).take s) := by
  simp [Array.pairwise_iff_getElem] at *
  grind

grind_pattern Array.pairwise_take_swap => Array.Pairwise (·  ·) ((arr.swap s t).take s)

theorem Array.pairwise_take_succ {α : Type} [LE α] [IsPartialOrder α] {arr : Array α}
  (k : Nat) (hk : k < arr.size)
  (h : Array.Pairwise (·  ·) (arr.take k))
  (le :  (i : Nat) (_ : i < arr.size), arr[i]  arr[k])
    : Array.Pairwise (·  ·) (arr.take (k + 1)) := by
  simp [Array.pairwise_iff_getElem] at *
  grind

grind_pattern Array.pairwise_take_succ => Array.Pairwise (·  ·) (arr.take (k + 1))

variable [LE α] [IsLinearOrder α] [LawfulOrderLT α]

theorem ICan'tBelieveItCanSort_sorted (arr : Array α) : ICan'tBelieveItCanSort arr |>.Pairwise (·  ·) := by
  generalize h : ICan'tBelieveItCanSort arr = x
  apply Id.of_wp_run_eq h
  mvcgen invariants
  · ⇓⟨cursor, vec => vec.take cursor.pos |>.toArray.Pairwise (·  ·)
  · ⇓⟨cursor, vec => by
    expose_names
    exact (vec.take cur |>.toArray.Pairwise (·  ·)) 
       i (_ : i < cursor.pos), vec[i]'(by grind)  vec[cur]'(by grind)
  with (simp at *; grind)

Last updated: Dec 20 2025 at 21:32 UTC