Zulip Chat Archive

Stream: new members

Topic: Equality problems for defined structures


Bashar Hamade (Jul 14 2024 at 20:49):

I'm struggling to understand what type of equality proofs I need when trying to write (basic) function definitions.I'm implementing labelled transition systems and I wanna write a function that gets the predecessor states of a given state using a given action :

def pre {lts : LTS} (s : { s : State // s  lts.states }) (a : Action) : List { s : State // s  lts.states } :=
  List.filter (fun st => (lts.transition st a).indexOf s != none ) lts.states

However I get this error :

failed to synthesize
  BEq { s // s  lts.states }
use `set_option diagnostics true` to get diagnostic information

What is exactly BEq ? and why does Lean struggle with such equalities ?

Bashar Hamade (Jul 14 2024 at 20:50):

here is the full code :

import Batteries.Data.List.Perm

open List

set_option autoImplicit true

@[ext]
structure State where
  val : String
deriving BEq

theorem State.beq_iff {s t : State} : (s == t)  s.val = t.val := by
  rw [show s == t  s.val == t.val from Iff.rfl, beq_iff_eq]


instance : LawfulBEq State where
  rfl := by simp [State.beq_iff]
  eq_of_beq h := State.ext _ _ (State.beq_iff.1 h)

structure Action where
  val : String



structure LTS where
  states : List State
  actions : List Action
  transition : State  Action  List { s : State // s  states }
  s0 : State

def ValidState (lts : LTS) :=
  { s : State // s  lts.states }

def post {lts : LTS} (s : { s : State // s  lts.states }) (a : Action) : List { s : State // s  lts.states } :=
  lts.transition s a


def post_S {lts : LTS} (states : List { s : State // s  lts.states }) (a : Action) : List { s : State // s  lts.states } :=
  states.foldl (fun acc state => acc ++ post state a) []

def post_A {lts : LTS} (s : { s : State // s  lts.states }) (actions : List Action) : List { s : State // s  lts.states } :=
  actions.foldl (fun acc act => acc ++ post s act) []

def post_S_A {lts : LTS} (states : List { s : State // s  lts.states }) (actions : List Action) : List { s : State // s  lts.states } :=
  actions.foldl (fun acc act => acc ++ post_S states act) []

def post_S_n {lts : LTS} (states : List { s : State // s  lts.states }) (actions : List Action) (n : Nat) : List { s : State // s  lts.states } :=
  match n with
  | 0 => states
  | n' + 1 => post_S_A (post_S_n states actions n') actions

theorem List.not_mem_of_indexOf?_eq_none [BEq α] [LawfulBEq α] (l : List α) (a : α)
    (ha : l.indexOf? a = none) : a  l := by
  induction l with
  | nil => simp
  | cons b t ih =>
    simp [indexOf?] at ha 
    split at ha
    · simp at ha
    · rw [Option.map_eq_none'] at ha
      refine Ne.symm ?_, ih ha
      next h =>
      rintro rfl
      exact h LawfulBEq.rfl

def reach_aux (lts : LTS) (visited : { l : List State // l <+~ lts.states } )
(to_visit : List { s : State // s  lts.states }) (acc : List State): List State :=
  match to_visit with
  | [] => acc
  | s::ss =>
    if h : visited.1.indexOf? s.1 != none then
      reach_aux lts visited ss acc
    else
      have hx : (s.1 :: visited.1) <+~ lts.states := by
        refine List.cons_subperm_of_not_mem_of_mem ?_ s.2 visited.2
        rw [Bool.not_eq_true, bne_eq_false_iff_eq] at h
        exact List.not_mem_of_indexOf?_eq_none _ _ h
      have : lts.states.length - (visited.1.length + 1) < lts.states.length - visited.1.length := by
        refine Nat.sub_add_lt_sub ?_ Nat.zero_lt_one
        simpa using hx.length_le
      reach_aux lts s::visited, hx (  (post_A  s lts.actions)  ++ ss) ( (post_A  s lts.actions).map Subtype.val ++ acc )
termination_by (lts.states.length - visited.1.length, to_visit.length)


def reach {lts : LTS} (initial_states : List { s : State // s  lts.states }) : List State :=
  reach_aux lts [], nil_subperm initial_states []




def pre {lts : LTS} (s : { s : State // s  lts.states }) (a : Action) : List { s : State // s  lts.states } :=
  List.filter (fun st => (lts.transition st a).indexOf s != none ) lts.states

Edward van de Meent (Jul 14 2024 at 21:33):

from my memory, BEq is a typeclass which carries a bool-valued 2-ary function on the supplied type. furthermore, LawfulBEq says that this boolean value corresponds to the Prop-valued equality given by the Eq type.

the problem is that because {s:State // s \in lts.states} is a separate type from State, it doesn't automatically have a BEq instance. (in general these instances can't. consider what would happen if we automatically supplied add to {n:Nat//n<4}). however, in this case you can fix this:

local instance instBEqSubtype {α : Type _} [BEq α] (P: α  Prop) : BEq (Subtype P) where
  beq a b := a.val == b.val

local instance instLawfulBEqSubtype {α :Type _} [BEq α] [i:LawfulBEq α] (P : α  Prop) :
    LawfulBEq (Subtype P) where
  eq_of_beq := by
    intro a b
    dsimp [BEq.beq]
    intro h
    ext
    exact i.eq_of_beq h
  rfl := by
    intro a
    dsimp [BEq.beq]
    exact i.rfl

Edward van de Meent (Jul 14 2024 at 21:34):

the local is because i'm not familiar enough with Batteries and friends to know that these issues don't cause any issues

Chris Bailey (Jul 14 2024 at 22:33):

There's an instance to get BEq from DecidableEq, and there's a DecidableEq on Subtype, but I guess there's nothing separate for BEq.

I think in general if the type allows (as State does here), you want to derive DecidableEq since you get more stuff for free.

Marcus Rossel (Jul 15 2024 at 06:25):

Edward van de Meent said:

the problem is that because {s:State // s \in lts.states} is a separate type from State, it doesn't automatically have a BEq instance. (in general these instances can't.

You can easily automatically derive BEq for subtypes though.

Bashar Hamade (Jul 15 2024 at 17:56):

Edward van de Meent said:

however, in this case you can fix this:

Sorry for late reply,but this still does not work and I get the same error . Also as @Marcus Rossel said that BEq can be easily automatically derived,could you elaborate more on how to do so?

Edward van de Meent (Jul 15 2024 at 18:08):

Bashar Hamade said:

Sorry for late reply,but this still does not work and I get the same error

are you sure? because for me, it gives a notably different error.

Bashar Hamade (Jul 15 2024 at 18:11):

ah sorry,got mixed up as I was getting the error I currently got at a previous time too. So now the error is :

application type mismatch
  filter (fun st  indexOf? s (lts.transition st.val a) != none) lts.states
argument
  lts.states
has type
  List State : Type
but is expected to have type
  List { s // s  lts.states } : Type

so it still can't properly derive the fact that lts.states are also of type List { s // s ∈ lts.states }

Bashar Hamade (Aug 03 2024 at 11:50):

one problem I'm facing is Lean wanting me to prove that an element of a List that has been constructed actually exists in it, is there a painless way to deal with this?

def states : List State :=
  [State.mk "UNUSED", State.mk "BURNING", State.mk "EXTINCT"]

def actions : List Action :=
  [Action.mk "strike", Action.mk "extinguish"]

def transition (s : State) (a : Action) : List { s : State // s  states } :=
  match (s.val, a.val) with
  | ("UNUSED", "strike") => [State.mk "BURNING", by simp [states]] -- here we use the constructor notation to provide a proof that State.mk "BURNING" is a member of `states`
  | ("BURNING", "extinguish") => [State.mk "EXTINCT", by simp [states]]
  | _ => []

def lts_example : LTS :=
  LTS.mk
    states
    actions
    transition
    <| State.mk "UNUSED"

-- the simp proof above doesn't work here for some reason
example : lts_example.post (State.mk "UNUSED", sorry) (Action.mk "strike") = [State.mk "BURNING", sorry] := sorry

Bashar Hamade (Aug 03 2024 at 11:50):

I was wondering if there is some instance that could be applied here that would make lean automatically understand this membership


Last updated: May 02 2025 at 03:31 UTC