Zulip Chat Archive

Stream: lean4

Topic: struggling with a proof


Arthur Paulino (Apr 06 2022 at 12:54):

I'm trying to clear out this sorry but I'm having two problems:

  • I'm not being able to use induction on l because Lean is complaining about a "type mismatch when assigning motive"
  • Even if I succeeded at using induction, I'm afraid I wouldn't be able to go much further because I don't see a reference to consume in the context:
ctx : Context
n : String
es : NEList Expression
ns : NEList String
h : NEList.noDup ns = true
p : Program
l? : Option (NEList String)
p : Program
hs : Option.isSome l? = true
l : NEList String := Option.get l? hs
 NEList.noDup l = true

consume is a function that consumes an NEList. So what I'm trying to prove is basically that if the initial NEList doesn't have duplicated values, then the resulting NEList won't either.

Leonardo de Moura (Apr 06 2022 at 14:30):

Disclaimer: I did not download/try your example on my machine. So, take the following suggestion with a grain of salt :)
The notation

        let (l?, p) := consume p ns es
        <rest>

is syntax sugar for

match consume p ns es with
| (l?, p) => <rest>

In the match-expression we can annotate discriminants to get a hypotheses stating they are equal to the pattern. Example:

match h : consume p ns es with
| (l?, p) => <rest>

If you use this annotation, we will have h : consume p ns es = (l?, p) in your local context.
We currently do not have a similar notation for the let syntax sugar above.

Leonardo de Moura (Apr 06 2022 at 14:38):

For the first issue, it seems to be a bug in the induction tactic when the variable is a let-declaration. I will take a look.
In the meantime, you can make progress by using let_fun instead

          let_fun l : NEList String := l?.get hs

let_fun x := v; b is sugar for (fun x => b) v

Leonardo de Moura (Apr 06 2022 at 14:48):

BTW, you can also simplify

if hs : l?.isSome then
          let l := l?.get hs
          have : l.noDup := sorry
          return curry l this p
        else
          return ( p.runIO ctx).2

to

match l? with
| some l =>
          have : l.noDup := sorry
          return curry l this p
 | none =>
          return ( p.runIO ctx).2

or

if let some l := l? then
          have : l.noDup := sorry
          return curry l this p
else
          return ( p.runIO ctx).2

Leonardo de Moura (Apr 06 2022 at 14:50):

You can also combine the consume match with the one above and write

        match h : consume p ns es with
        |  (some l, p) =>
            have : l.noDup := sorry
            curry l this p
        | _ => (p.run ctx).2

Arthur Paulino (Apr 06 2022 at 15:07):

Yeah that's precisely how it was before but I changed it as an attempt to get consume in my context

Leonardo de Moura (Apr 06 2022 at 15:09):

Note that, you will now have h : consume p ns es = ...

Arthur Paulino (Apr 06 2022 at 15:15):

Oh right! It's actually slightly different. Thanks!!

Arthur Paulino (Apr 06 2022 at 15:20):

I will try it when I get home

Arthur Paulino (Apr 06 2022 at 20:56):

Not sure if this is hard, or tricky, or if I'm simply not competent enough, but how would you prove this one?

theorem eqqIffEq [BEq α] {a b : α} : a == b  a = b := sorry

Mauricio Collares (Apr 06 2022 at 21:01):

It's provable if you replace BEq by LawfulBEq, but you're out of luck if your BEq instance is not lawful.

Arthur Paulino (Apr 06 2022 at 21:14):

Thanks! I didn't know about LawfulBEq!

theorem eqIffBEq [BEq α] [LawfulBEq α] {a b : α} : a == b  a = b := by
  constructor
  · intro h; exact eq_of_beq h
  · intro h; simp [h]

Arthur Paulino (Apr 07 2022 at 14:07):

Why is it that sometimes we end up with infeasible inductive hypotheses? Is it a symptom os flawed definitions? In this example I can't seem to be able to use hn without assuming something false:

inductive NEList (α : Type)
  | uno  : α  NEList α
  | cons : α  NEList α  NEList α

def NEList.contains [BEq α] : NEList α  α  Bool
  | uno a,     x => a == x
  | cons a as, x => a == x || as.contains x

def NEList.noDupAux [BEq α] (l : List α) : NEList α  Bool
  | .uno  a   => ¬ l.contains a
  | .cons a r => ¬ l.contains a && ¬ r.contains a && noDupAux (a :: l) r

def NEList.noDup [BEq α] (l : NEList α) : Bool :=
  noDupAux [] l

mutual

  inductive Value
    | nil   : Value
    | bool  : Bool         Value
    | int   : Int          Value
    | float : Float        Value
    | list  : Array Value  Value
    | str   : String       Value
    | error : String       Value
    | curry : (l : NEList String)  l.noDup  Program  Value
    deriving Inhabited

  inductive Expression
    | atom : Value       Expression
    | var  : String      Expression
    | not  : Expression  Expression
    | app  : String      NEList Expression  Expression
    | add  : Expression  Expression  Expression
    | mul  : Expression  Expression  Expression
    | eq   : Expression  Expression  Expression
    | ne   : Expression  Expression  Expression
    | lt   : Expression  Expression  Expression
    | le   : Expression  Expression  Expression
    | gt   : Expression  Expression  Expression
    | ge   : Expression  Expression  Expression

  inductive Program
    | skip        : Program
    | sequence    : Program     Program  Program
    | attribution : String      Program  Program
    | ifElse      : Expression  Program  Program  Program
    | whileLoop   : Expression  Program  Program
    | evaluation  : Expression  Program
    | fail        : String      Program
    deriving Inhabited

end

open NEList Program in
def consume (p : Program) :
    NEList String  NEList Expression  (Option (NEList String)) × Program
  | cons n ns, cons e es =>
    consume (sequence (attribution n (evaluation e)) p) ns es
  | cons n ns, uno  e    => (some ns, sequence (attribution n (evaluation e)) p)
  | uno  n,    uno  e    => (none, sequence (attribution n (evaluation e)) p)
  | uno  _,    cons ..   => (none, fail "incompatible number of parameters")

theorem noDupOfNoDupAux {ns : NEList α} [BEq α] (h : NEList.noDupAux l ns) :
    ns.noDup := sorry -- this is fine

theorem noDupOfConsumeNoDup
  (h : NEList.noDup ns) (h' : consume p' ns es = (some l, p)) :
    NEList.noDup l = true := by
    induction ns with
    | uno n =>
      cases es with
      | _ => simp [consume] at h'
    | cons n ns hn =>
      simp [NEList.noDup, NEList.noDupAux] at h
      have hn := hn $ noDupOfNoDupAux h.2
      have : consume p' (NEList.cons n ns) es = consume p' ns es := by
        -- `(NEList.cons n ns)` can't be defeq to `ns`... not fine!
        sorry
      rw [this] at h'
      exact hn h'

Any ideas what I might be doing wrong?

Mario Carneiro (Apr 07 2022 at 15:47):

the issue is usually a missing generalizing on induction

Arthur Paulino (Apr 07 2022 at 15:55):

Thanks!!

Mario Carneiro (Apr 07 2022 at 16:02):

theorem noDupOfConsumeNoDup
  (h : NEList.noDup ns) (h' : consume p' ns es = (some l, p)) :
    NEList.noDup l = true := by
    induction ns generalizing p' es with
    | uno n => cases es <;> cases h'
    | cons n ns ih =>
      simp [NEList.noDup, NEList.noDupAux] at h
      have h := noDupOfNoDupAux h.2
      cases es with
      | uno e => cases h'; exact h
      | cons e es => exact ih h h'

Arthur Paulino (May 27 2022 at 23:57):

I have the impression that this should be simple, but I don't know what I'm missing

inductive Foo
  | foo : Nat  Foo
  | foos : Array Foo  Foo
  deriving BEq

example : Foo.foo 0  Foo.foo 1 := by simp
-- Goals accomplished 🎉

example : #[0]  #[1] := by simp
-- Goals accomplished 🎉

example : #[Foo.foo 0]  #[Foo.foo 1] := by simp
-- unsolved goals
-- ⊢ ¬#[Foo.foo 0] = #[Foo.foo 1]

example : Foo.foos #[.foo 0]  Foo.foos #[.foo 1] := by simp
-- unsolved goals
-- ⊢ ¬#[Foo.foo 0] = #[Foo.foo 1]

Can someone help me please? :pray:

Siddharth Bhat (May 28 2022 at 00:03):

inductive Foo
  | foo : Nat  Foo
  | foos : Array Foo  Foo
  deriving BEq

example : Foo.foo 0  Foo.foo 1 := by simp
-- Goals accomplished 🎉

example : #[0]  #[1] := by simp
-- Goals accomplished 🎉

example : #[Foo.foo 0]  #[Foo.foo 1] := by {
  unfold Not;
  intros CONTRA;
  exact (nomatch CONTRA);
}
-- goals accomplished

example : Foo.foos #[.foo 0]  Foo.foos #[.foo 1] :=
by {
  unfold Not;
  intros CONTRA;
  exact (nomatch CONTRA);
}
-- goals accomplished

Siddharth Bhat (May 28 2022 at 00:04):

I doubt this is the best way to write this in lean. I wrote it as I would in Coq, which is to unfold the Not, intros the absurd hypothesis, and then claim that the absurdity is vacuously true.

Arthur Paulino (May 28 2022 at 00:06):

Does your kernel accept it? Mine doesn't: leanprover/lean4:nightly-2022-05-18

Siddharth Bhat (May 28 2022 at 00:07):

I was on leanprover/lean4:nightly-2022-03-30. Let me try on a newer version.

Siddharth Bhat (May 28 2022 at 00:08):

inductive Foo
  | foo : Nat  Foo
  | foos : Array Foo  Foo
  deriving BEq

example : Foo.foo 0  Foo.foo 1 := by simp
-- Goals accomplished 🎉

example : #[0]  #[1] := by simp
-- Goals accomplished 🎉

example : #[Foo.foo 0]  #[Foo.foo 1] := fun CONTRA => nomatch CONTRA

example : Foo.foos #[.foo 0]  Foo.foos #[.foo 1] := fun CONTRA => nomatch CONTRA

This formulation works on leanprover/lean4:nightly-2022-05-18

Siddharth Bhat (May 28 2022 at 00:10):

example : #[Foo.foo 0]  #[Foo.foo 1] := by {
  intros CONTRA;
  exact (nomatch CONTRA);
}

in proof mode also works for me on Lean (version 4.0.0-nightly-2022-05-18, commit eb170d1f43e8, Release). Does it for you, @Arthur Paulino ?

Arthur Paulino (May 28 2022 at 00:11):

Yeah

example : #[Foo.foo 0]  #[Foo.foo 1] := by
  intro h
  exact nomatch h

Arthur Paulino (May 28 2022 at 00:11):

Thanks!!

Leonardo de Moura (May 28 2022 at 01:28):

@Arthur Paulino Thanks for pointing it out. I added the missing simp theorem.

List.of_toArray_eq_toArray (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) :=

All examples should now go through using by simp.


Last updated: Dec 20 2023 at 11:08 UTC