Zulip Chat Archive

Stream: lean4

Topic: induction for custom dependent type


Abdullah Al Kayal (Feb 02 2025 at 14:23):

I have defined an inductive type called Circuit with the following implementation:

inductive Circuit : Nat  Type
| wire : Circuit 1
| empty : Circuit 0
| tof : Circuit 3
| swap {n : Nat} (f1 : Fin n) (f2 : Fin n) : Circuit n
| comp {n : Nat} (c1 : Circuit n) (c2 : Circuit n) : Circuit n
| tensorProduct {n m : Nat} (c1 : Circuit n) (c2 : Circuit m) : Circuit (n + m)

notation "∅" => empty

I am trying to prove the following theorem:

theorem circ_zero_empty (c : Circuit 0) : c =  :=
by
  cases c
  ...

There are three problems with this:

  1. When I apply the cases tactic on c, I only get the empty, swap and comp cases, but I don't get the tensorProduct one.
  2. I am trying to use induction instead of cases, as I don't know how to prove the comp or tensorProduct cases without an induction hypothesis, but when I try to do so, I get the following error:
index in target's type is not a variable (consider using the `cases` tactic instead)
  0

I understand that c has type Circuit 0 and not Circuit n but I don't understand why this would make induction not applicable to it.

  1. Even when I try to prove the trivial case ∅ = ∅, I get the following error:
theorem circ_zero_empty (c : Circuit 0) : c =  :=
by
  cases c with
  | empty =>
    rfl
    /-
    dependent elimination failed, failed to solve equation
    0 = n✝.add m✝
    -/

Aaron Liu (Feb 02 2025 at 14:34):

cases doesn't give you the tensorProduct case because it doesn't know if 0 = n + m is a valid combination. induction isn't smart enough yet to figure out how to do induction on Circuit 0, it only knows how to do Circuit n.
You can turn Circuit 0 into Circuit n with generalize h : 0 = n at c, and you get c : Circuit n and h : n = 0. Then, you can do induction c to induct on c.

Aaron Liu (Feb 02 2025 at 14:35):

See #new members > Type mismatch when matching inductive dependent type

Edward van de Meent (Feb 02 2025 at 14:36):

alternatively, it might be easier to work with a version of the type where there is no natural number in the type, and rather define a degree or rank (or whatever it's called) function Circuit -> Nat

Abdullah Al Kayal (Feb 02 2025 at 14:46):

@Aaron Liu are you telling me to use recursion instead of induction. I've seen this page that explains it, but how can I use it in this case?

Abdullah Al Kayal (Feb 02 2025 at 14:48):

@Edward van de Meent do you mean define it in a similar fashion to e.g. List.Vector?

Edward van de Meent (Feb 02 2025 at 14:50):

yes, something like this:

set_option autoImplicit false

inductive Circuit' : Type
| wire : Circuit'
| empty : Circuit'
| tof : Circuit'
| swap {n : Nat} (f1 : Fin n) (f2 : Fin n) : Circuit'
| comp (c1 : Circuit') (c2 : Circuit') : Circuit'
| tensorProduct (c1 : Circuit') (c2 : Circuit') : Circuit'

def Circuit'.degree (c:Circuit') : Nat := match c with
  | .wire => 1
  | .empty => 0
  | .tof => 3
  | @swap n _ _ => n
  | .comp l r => max l.degree r.degree
  | .tensorProduct l r => l.degree + r.degree

/-- a `Circuit'` is well-formed when every constituent `comp` constructor uses degrees of the right size. -/
inductive Circuit'.wf : Circuit'  Prop
  | isWire : wire.wf
  | isEmpty : empty.wf
  | isTof : tof.wf
  | isSwap {n} (f1 f2 : Fin n) : (swap f1 f2).wf
  | isComp {l r : Circuit'} (hl : l.wf) (hr : r.wf) (heq : l.degree = r.degree) : (comp l r).wf
  | isTensorProduct {l r : Circuit'} (hl : l.wf) (hr : r.wf): (tensorProduct l r).wf

def Circuit (n:Nat) : Type := {c:Circuit' // c.wf  c.degree = n}

Edward van de Meent (Feb 02 2025 at 15:02):

ah, i just realised this will not actually help you prove the theorem you're trying to prove, since .comp .empty .empty will still be different from .empty

Aaron Liu (Feb 02 2025 at 15:04):

This is suspicious

theorem not_circ_zero_empty : ¬∀ (c : Circuit 0), c =  := by
  intro h
  specialize h (.comp  )
  simp at h

Edward van de Meent (Feb 02 2025 at 15:04):

well, that's just how the defined type works... perhaps it might be useful if you would give a reference as to what is being modeled (or rather, what this is supposed to model)

Abdullah Al Kayal (Feb 02 2025 at 15:34):

Aaron Liu said:

This is suspicious

theorem not_circ_zero_empty : ¬∀ (c : Circuit 0), c =  := by
  intro h
  specialize h (.comp  )
  simp at h

That confused me even more :smiling_face_with_tear: . Does that then imply that what I'm trying to prove is wrong in the first place. But then I don't really understand why it would be.

My goal state after specialize h (.comp ∅ ∅)is

h : (∅∘∅) = 
 False

Does that imply that (∅∘∅) = ∅ is a contradiction?

Aaron Liu (Feb 02 2025 at 15:34):

Yes

Abdullah Al Kayal (Feb 02 2025 at 15:34):

But why.

Aaron Liu (Feb 02 2025 at 15:35):

You have comp _ _ = empty

Edward van de Meent (Feb 02 2025 at 15:35):

because by definition of inductive types, the things you construct with .comp are different from those you construct with .empty

Abdullah Al Kayal (Feb 02 2025 at 15:35):

oh is that it

Aaron Liu (Feb 02 2025 at 15:36):

Because you can distinguish comp from empty by pattern matching, so they can't be the same

Abdullah Al Kayal (Feb 02 2025 at 15:36):

Is there some way around it (besides defining it like @Edward van de Meent suggested)?

Edward van de Meent (Feb 02 2025 at 15:36):

my suggestion doesn't fix this, unfortunately

Edward van de Meent (Feb 02 2025 at 15:37):

what are you trying to model with the Circuit type? is there some literature you're following?

Abdullah Al Kayal (Feb 02 2025 at 15:38):

There isn't really any literature that I'm closely following, I am just trying to define the set of reversable computations using this Circuit definition.

Abdullah Al Kayal (Feb 02 2025 at 15:39):

Anyway thank you guys, I guess I'll have to rework on my definitions then.

Edward van de Meent (Feb 02 2025 at 15:42):

thinking about it, there might be a way to do this... if there is such a thing as a normal form of these circuits, you can create a predicate saying an element of Circuit' is in normal form, then add that to the required proofs for Circuit

Aaron Liu (Feb 02 2025 at 15:43):

You could define a predicate relation saying that two circuits are "the same", and quotient by that predicate relation.

Edward van de Meent (Feb 02 2025 at 15:44):

(quotients are worse to work with than subtypes, fyi)

Edward van de Meent (Feb 02 2025 at 15:53):

ah, i think i finally know what you're trying to model:

structure Circuit where
  size : Nat
  -- equiv : Fin size ≃ Fin size -- use this rather than the following when using mathlib
  toFun : Fin size  Fin size
  invFun : Fin size  Fin size
  left_inv :  (i : Fin size), toFun (invFun i) = i
  right_inv :  (i : Fin size), invFun (toFun i) = i

you don't get induction automatically this way, however

Aaron Liu (Feb 02 2025 at 15:55):

what is tof : Circuit 3

Aaron Liu (Feb 02 2025 at 15:55):

My guess is some kind of junction

Edward van de Meent (Feb 02 2025 at 15:56):

true or false?

Edward van de Meent (Feb 02 2025 at 15:56):

:thinking: but then my model is still missing something

Edward van de Meent (Feb 02 2025 at 15:57):

i'm guessing it's a trinary operator (with 3 resulting arguments) which swaps b and c depending on if a is true or not

Edward van de Meent (Feb 02 2025 at 16:01):

so then, a maybe more accurate representation would be this:

structure Circuit where
  size : Nat
  -- equiv : (Fin size → Bool) ≃ (Fin size → Bool) -- use this rather than the following when using mathlib
  toFun : (Fin size  Bool)  (Fin size  Bool)
  invFun : (Fin size  Bool)  (Fin size  Bool)
  left_inv :  (v : Fin size  Bool), toFun (invFun v) = v
  right_inv :  (v : Fin size  Bool), invFun (toFun v) = v

Kyle Miller (Feb 02 2025 at 17:24):

I don't see another trick mentioned, which is to embed the equality in the type, saving you from needing to generalize:

inductive Circuit : Nat  Type
| wire : Circuit 1
| empty : Circuit 0
| tof : Circuit 3
| swap {n : Nat} (f1 : Fin n) (f2 : Fin n) : Circuit n
| comp {n : Nat} (c1 : Circuit n) (c2 : Circuit n) : Circuit n
| tensorProduct {n m nm : Nat} (c1 : Circuit n) (c2 : Circuit m) (h : n + m = nm) : Circuit nm

Kyle Miller (Feb 02 2025 at 17:25):

The rule is "cases will work if all the indices appearing in constructors are made of free variables and constructor applications (after unfolding everything)". The issue is that + is not a constructor.


Last updated: May 02 2025 at 03:31 UTC