Zulip Chat Archive

Stream: Program verification

Topic: Induction over RTC


Andrew (Jul 17 2025 at 13:39):

Hi! I am writing a program logic that defines small-step semantics as follows:

abbrev Configuration := Command × Stack × Trace
inductive small_step : Configuration -> Configuration -> Prop
...

I define a reflexive transitive closure over the small step like this:

inductive rtc (r : α  α  Prop) : α -> α  Prop
| refl (x : α) : rtc r x x
| step (x y z : α) : r x y -> rtc r y z  rtc r x z

def small_step_rtc := rtc small_step

Now, I have a hypothesis of the form:

(skip, σ, τ) ⇝* (skip, σ', τ')

where ⇝* is the small-step reflexive transitive closure. But when I try to perform an induction on this hypothesis, I get:

Invalid target: Index in target's type is not a variable (consider using the `cases` tactic instead)
  (skip, σ, τ)

Is there any way to do this induction without this error? I have tried generalizing and rewriting away my triples, but this is an enormous hassle. Given that I will have to do many inductions of this form, it seems insane to me that I will have to use a workaround for a very simple use-case

Malvin Gattinger (Jul 17 2025 at 13:43):

There is docs#Relation.ReflTransGen in mathlib, coming with several useful lemmas and induction principles like docs#Relation.ReflTransGen.head_induction_on that you can use with induction some_rtc_hyp using Relation.ReflTransGen.head_induction_on. Maybe use that or have a look how those are proven? (And in my own project here in Pdl.Star are some more lemmas about it.)

An #mwe in one code block that workds in the live-lean browser thingy would be good to help you better.

Andrew (Jul 17 2025 at 13:50):

Unfortunately the ReflTransGen is in the wrong order (I'm looking for R a b -> RTC R b c -> R a c whereas ReflTransGen does RTC R a b -> R b c -> RTC R a c)

Andrew (Jul 17 2025 at 13:51):

I'm currently loking over how to use head_induction_on

Andrew (Jul 17 2025 at 13:53):

This is the #mwe if you'd care to have a look. It seems to be a known limitation of lean that you can't do induction on tuples https://live.lean-lang.org/#codez=JYWwDg9gTgLgBAWQIYwBYBtgCMB0AZCAc2AGMcAlAU3RWAgDsAoRgE0oDM4BnAVxDgBccAHIo4AdZFiAtAD4pMRgB84ACiQAaOFgCUcALzykcANTbm6SiBDH2ECIO581m7Xv1P+qrFqR65cABiSOhclIIeWACejHBwwPQwUBBccKix8fQsPCQwdPRpcISU9JRQIcAAXgmEcMZYGVzQUFFAA

Malvin Gattinger (Jul 17 2025 at 13:56):

What would the induction proof here be in English? Why do you want induction in the first place? The sum type here is not inductive, i.e. it does not have values that contain values of the sum type itself, right?

Aaron Liu (Jul 17 2025 at 13:56):

Andrew said:

Unfortunately the ReflTransGen is in the wrong order (I'm looking for R a b -> RTC R b c -> R a c whereas ReflTransGen does RTC R a b -> R b c -> RTC R a c)

that's a theorem

Aaron Liu (Jul 17 2025 at 13:57):

Andrew said:

It seems to be a known limitation of lean that you can't do induction on tuples

this feels false

Andrew (Jul 17 2025 at 13:57):

Malvin Gattinger said:

What would the induction proof here be in English? Why do you want induction in the first place? The sum type here is not inductive, i.e. it does not have values that contain values of the sum type itself, right?

This example doesn't have anything to do with what I'm actually doing, it just surfaces the error I'm running into

Malvin Gattinger (Jul 17 2025 at 13:58):

Can you give us a more realistic example then? The lemma here seems false because sum (1,2) = sum (2,1) right? And have you tried using cases instead?

Andrew (Jul 17 2025 at 13:59):

I can't use cases, because in this example I need the inductive hypothesis

Andrew (Jul 17 2025 at 13:59):

It genuinely doesn't matter what I'm doing, i just need to be able to do an induction in which the hypothesis contains a tuple

Aaron Liu (Jul 17 2025 at 13:59):

I will need more context

Andrew (Jul 17 2025 at 14:00):

If you see the error in the #mwe, I'm just trying to get the induction tactic to not fail

Aaron Liu (Jul 17 2025 at 14:00):

induction on tuples is complicated and it depends on what kind of induction hypotheses you need and whether you are inducting on Props or Types

Aaron Liu (Jul 17 2025 at 14:01):

Andrew said:

If you see the error in the #mwe, I'm just trying to get the induction tactic to not fail

not sure what you want to accomplish here

Andrew (Jul 17 2025 at 14:01):

So in my example what I have is a triple:

hstep : (skip, σ, τ) ⇝* (skip, σ', τ')

Aaron Liu (Jul 17 2025 at 14:01):

your goal is false and there aren't any inductive hypotheses

Andrew (Jul 17 2025 at 14:01):

Here skip is a constant and sigma and tau are variables

Aaron Liu (Jul 17 2025 at 14:01):

oh I see

Andrew (Jul 17 2025 at 14:02):

What I want is to just get induction hstep to go through

Aaron Liu (Jul 17 2025 at 14:03):

I would do generalize ha : (skip, σ, τ) = a, hb : (skip, σ', τ') = b at hstep followed by induction hstep generalizing ... for a sufficient amount of generalizing

Aaron Liu (Jul 17 2025 at 14:03):

it really depends on what you want the induction hypothesis to look like

Andrew (Jul 17 2025 at 14:03):

Yeah so that was my original question

Andrew (Jul 17 2025 at 14:04):

What I currently have is this:

  generalize  : (skip, σ, τ) = π
  generalize hπ' : (skip, σ', τ') = π'
  rw [, hπ'] at hstep
  induction hstep

Malvin Gattinger (Jul 17 2025 at 14:04):

import Mathlib.Logic.Relation

def Command : Type := sorry
def Stack : Type := sorry
def Trace : Type := sorry

abbrev Configuration := Command × Stack × Trace

def small_step : Configuration -> Configuration -> Prop := sorry

def big_step := Relation.ReflTransGen small_step

def isNice : Configuration  Prop := sorry

theorem something (h : big_step a b ) : isNice b := by
  induction h
  case refl =>
    sorry
  case tail IH =>
    sorry

theorem something_head (h : big_step a b ) : isNice b := by
  induction h using Relation.ReflTransGen.head_induction_on
  case refl =>
    sorry
  case head IH =>
    sorry

Andrew (Jul 17 2025 at 14:04):

I'm trying to figure out if there's either an idiomatic or less repetitive way of doing this

Aaron Liu (Jul 17 2025 at 14:05):

yeah I have nothing

Andrew (Jul 17 2025 at 14:08):

Thanks for the support both of you

Andrew (Jul 17 2025 at 14:08):

Malvin Gattinger said:

import Mathlib.Logic.Relation

def Command : Type := sorry
def Stack : Type := sorry
def Trace : Type := sorry

abbrev Configuration := Command × Stack × Trace

def small_step : Configuration -> Configuration -> Prop := sorry

def big_step := Relation.ReflTransGen small_step

def isNice : Configuration  Prop := sorry

theorem something (h : big_step a b ) : isNice b := by
  induction h
  case refl =>
    sorry
  case tail IH =>
    sorry

theorem something_head (h : big_step a b ) : isNice b := by
  induction h using Relation.ReflTransGen.head_induction_on
  case refl =>
    sorry
  case head IH =>
    sorry

Im going to see if I can refactor my proof to make this work

Andrew (Jul 17 2025 at 14:09):

Aaron Liu said:

Andrew said:

Unfortunately the ReflTransGen is in the wrong order (I'm looking for R a b -> RTC R b c -> R a c whereas ReflTransGen does RTC R a b -> R b c -> RTC R a c)

that's a theorem

Can you point me at any way to make the RTC work in the order I want? I'd like to use the Mathlib one if at all possible

Aaron Liu (Jul 17 2025 at 14:10):

docs#Relation.ReflTransGen.head is the theorem

Malvin Gattinger (Jul 17 2025 at 14:11):

In my example above please place the cursor after case head IH =>and look at the info view :)

Aaron Liu (Jul 17 2025 at 14:11):

there's also docs#Relation.ReflTransGen.trans and docs#Relation.ReflTransGen.single

Aaron Liu (Jul 17 2025 at 14:11):

there's a lot of theorems

Andrew (Jul 17 2025 at 14:11):

Thanks

Andrew (Jul 17 2025 at 14:11):

That's a huge help

Aaron Liu (Jul 17 2025 at 14:11):

you're probably better off looking them up on your own rather than having me list them off


Last updated: Dec 20 2025 at 21:32 UTC