Zulip Chat Archive

Stream: Is there code for X?

Topic: Formalizing array manipulations


Vlad (Apr 22 2024 at 21:30):

In my programming tasks, I frequently work with arrays. Below is an example of the kind of manipulations I'm interested in performing.

The current proof for this manipulation appears cumbersome and contains gaps.

Is there a more efficient way to streamline this type of work?

Specifically, I'm wondering if using Finset.sup' is a good approach.

Also, I understand that using Fin for indexing would eliminate the need for index boundary proofs, it has the limitation of only ranging from zero. My use cases often require indexing that starts from values above zero.

As a side note, I've customized the syntax for Finset.sup and Finset.sup' for better readability. Finset.sup' is further distinguished by including the hypothesis [H] next to the arrow.

import Lean
import Mathlib.Tactic

open Lean Elab Term Meta
open Finset
open BigOperators
open Algebra


namespace BigOperators
open Std.ExtendedBinder PrettyPrinter Delaborator SubExpr

scoped syntax (name := bigmax) "⇑ " ("[" term "]" )? extBinder ", " term:67 : term
scoped macro_rules (kind := bigmax)
  | `( $x:ident, $p) => `(Finset.sup Finset.univ (fun $x:ident  $p))
  | `( $x:ident : $t, $p) => `(Finset.sup Finset.univ (fun $x:ident : $t  $p))
  | `( [$hne] $x:ident : $t, $p ) => `(Finset.sup' Finset.univ $hne (fun $x:ident : $t  $p))

scoped syntax (name := bigmaxin) "⇑ " extBinder " in " term ", " term:67 : term
scoped macro_rules (kind := bigmaxin)
  | `( $x:ident in $s, $r) => `(Finset.sup $s (fun $x  $r))
  | `( $x:ident : $t in $s, $p) => `(Finset.sup $s (fun $x:ident : $t  $p))

@[scoped delab app.Finset.sup] def delabFinsetSup : Delab := whenPPOption getPPNotation do
  let #[_, _, _, _, s, f] := ( getExpr).getAppArgs | failure
  guard <| f.isLambda
  let ppDomain  getPPOption getPPPiBinderTypes
  let (i, body)  withAppArg <| withBindingBodyUnusedName fun i => do
    return (i,  delab)
  if s.isAppOfArity ``Finset.univ 2 then
    let binder 
      if ppDomain then
        let ty  withNaryArg 1 delab
        `(extBinder| $(.mk i):ident : $ty)
      else
        `(extBinder| $(.mk i):ident)
    `( $binder, $body)
  else
    let ss  withNaryArg 4 <| delab
    `( $(.mk i):ident in $ss, $body)
end BigOperators


theorem Finset.sup'_add_const {α : Type}(s: Finset α) {H: univ.Nonempty} (f : α  ) (bdd:  a  s,  b  s, f b  f a) (c : ) :
  ([H] a: s, (f a + c) ) = ([H] a: s, f a) + c
  := by
  apply le_antisymm
  . apply sup'_le
    intros
    rename_i b _
    gcongr
    simp_all only [univ_eq_attach, mem_attach, le_sup'_iff, true_and, Subtype.exists, exists_prop]
    use b
    simp_rw [le_refl, coe_mem];
    trivial
  . simp_all only [univ_eq_attach, le_sup'_iff, mem_attach, add_le_add_iff_right, sup'_le_iff,
    forall_true_left, Subtype.forall, true_and, Subtype.exists, exists_prop]


example
  (xs :Array )
  (i: )
  (hi1: i < xs.size)
  (hi2: 0 < i)
  {H: univ.Nonempty}
        :  a : Ico 0 i, ( b : Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry): WithBot )
        = [H] a : Ico 0 i, (([(by sorry)] b: Ico a.1 i, (xs[b.1]'sorry)) - xs[a.1]'sorry) -- how to move (by sorry) to the list of parameters ?
 := by
  calc  a : Ico 0 i, ( b : Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry):WithBot )
    _ = [H] a : Ico 0 i, ( b: Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry))      := by rw [ sup'_eq_sup];
    _ = [H] a : Ico 0 i, ([?_] b: Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry)) := by congr! 1; rw [sup'_eq_sup]; rw [univ_eq_attach, attach_nonempty_iff, nonempty_Ico]; sorry
    _ = [H] a : Ico 0 i, ([?_] b: Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry))  := by  rw [coe_sup', sup'_eq_sup];
                                                                                        simp only [univ_eq_attach, Function.comp_apply]
                                                                                        simp; congr! 1; rw[sup'_eq_sup]; congr
                                                                                        assumption;
                                                                                        simp_all only [univ_eq_attach]
                                                                                        simp_all only [attach_nonempty_iff, nonempty_Ico];
                                                                                        let x:= a.prop;
                                                                                        simp only [Nat.Ico_zero_eq_range, mem_range] at x
                                                                                        assumption
    _ = [H] a : Ico 0 i, (([?_] b: Ico a.1 i, (xs[b.1]'sorry)) - xs[a.1]'sorry)   :=  by congr! with a a';
                                                                                           conv =>
                                                                                             lhs
                                                                                             enter [3, b]
                                                                                             rw [Int.sub_eq_add_neg];
                                                                                           conv =>
                                                                                            lhs
                                                                                            rw [Finset.sup'_add_const _ (fun b => xs[b]'sorry) ]
                                                                                            . skip
                                                                                            . tactic => sorry

  let x:=a.prop; simp at x ⊢; assumption
  rfl

A. (Apr 23 2024 at 00:17):

I'd agree that your sup'_add_const looks overwrought given that this is possible:

theorem sup'_add_const {α : Type} {s : Finset α} (H : s.Nonempty) (f : α  ) (c : ) :
    (sup' _ H (f · + c) ) = (sup' _ H f) + c :=
  (comp_sup'_eq_sup'_comp H (· + c) (sup_add · · c)).symm

Vlad (Apr 23 2024 at 16:27):

Inspired by Alistair's answer, here's a bit shorter proof. Still quite long for such a trivial example

import Lean
import Mathlib.Tactic

open Lean Elab Term Meta
open Finset
open BigOperators
open Algebra


namespace BigOperators
open Std.ExtendedBinder PrettyPrinter Delaborator SubExpr

scoped syntax (name := bigmax) "⇑ " ("[" term "]" )? extBinder ", " term:67 : term
scoped macro_rules (kind := bigmax)
  | `( $x:ident, $p) => `(Finset.sup Finset.univ (fun $x:ident  $p))
  | `( $x:ident : $t, $p) => `(Finset.sup Finset.univ (fun $x:ident : $t  $p))
  | `( [$hne] $x:ident : $t, $p ) => `(Finset.sup' Finset.univ $hne (fun $x:ident : $t  $p))

scoped syntax (name := bigmaxin) "⇑ " extBinder " in " term ", " term:67 : term
scoped macro_rules (kind := bigmaxin)
  | `( $x:ident in $s, $r) => `(Finset.sup $s (fun $x  $r))
  | `( $x:ident : $t in $s, $p) => `(Finset.sup $s (fun $x:ident : $t  $p))

@[scoped delab app.Finset.sup] def delabFinsetSup : Delab := whenPPOption getPPNotation do
  let #[_, _, _, _, s, f] := ( getExpr).getAppArgs | failure
  guard <| f.isLambda
  let ppDomain  getPPOption getPPPiBinderTypes
  let (i, body)  withAppArg <| withBindingBodyUnusedName fun i => do
    return (i,  delab)
  if s.isAppOfArity ``Finset.univ 2 then
    let binder 
      if ppDomain then
        let ty  withNaryArg 1 delab
        `(extBinder| $(.mk i):ident : $ty)
      else
        `(extBinder| $(.mk i):ident)
    `( $binder, $body)
  else
    let ss  withNaryArg 4 <| delab
    `( $(.mk i):ident in $ss, $body)
end BigOperators


theorem Finset.sup'_add_const {α : Type} {s : Finset α} (H : s.Nonempty) (f : α  ) (c : ) :
    (sup' _ H (f · + c) ) = (sup' _ H f) + c :=
    (comp_sup'_eq_sup'_comp H (· + c) (sup_add · · c)).symm

theorem Finset.sup'_const_add {α : Type} {s: Finset α} (H : s.Nonempty) (f : α  ) (c : ) :
  (sup' _ H (c + f ·) ) = c + (sup' _ H f) :=
  by
  rw [Int.add_comm, (sup'_add_const H _ c)]
  simp [Int.add_comm]

example
  (xs :Array )
  (i: )
  (hi1: i < xs.size)
  (hi2: 0 < i)
  {H: univ.Nonempty}
        :  a : Ico 0 i, ( b : Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry): WithBot )
        = (([H] a : Ico 0 i, (([(by sorry)] b: Ico a.1 i, (xs[b.1]'sorry)) - xs[a.1]'sorry)):)
 := by
  calc  a : Ico 0 i, ( b : Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry):WithBot )
    _ = [H] a : Ico 0 i, ( b: Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry))      := by rw [ sup'_eq_sup];
    _ = [H] a : Ico 0 i, ([?_] b: Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry)) := by congr! 1; rw [sup'_eq_sup]; rw [univ_eq_attach, attach_nonempty_iff, nonempty_Ico]; sorry
    _ = [H] a : Ico 0 i, ([?_] b: Ico a.1 i, (xs[b.1]'sorry - xs[a.1]'sorry))  := by  rw [coe_sup', sup'_eq_sup];
                                                                                        simp only [univ_eq_attach, Function.comp_apply, coe_sup']
                                                                                        congr! 1; rw [sup'_eq_sup]
                                                                                        congr!
                                                                                        trivial
                                                                                        sorry
    _ = [H] a : Ico 0 i, (([?_] b: Ico a.1 i, (xs[b.1]'sorry)) - xs[a.1]'sorry)   :=  by congr! with a a';
                                                                                           simp_rw [Int.sub_eq_add_neg]
                                                                                           rw [sup'_add_const _ _ (-xs[a.1]'sorry)]
                                                                                           let x:=a.prop; simp at x ⊢; assumption

  rfl

Last updated: May 02 2025 at 03:31 UTC