Zulip Chat Archive

Stream: lean4

Topic: Tree with fixed branching


Tomas Skrivan (Apr 02 2022 at 18:38):

I'm trying to define data structure for trees with fixed branching factor. With the help of this question I have managed to get it compile, but I'm confused why I have to use Vector and can't use NArray

inductive Vector (α : Type u) : Nat  Type u where
  | nil  : Vector α 0
  | cons : α  Vector α n  Vector α (n+1)

def NArray (α : Type u) (n : Nat) : Type u := {u : Array α // u.size = n}

inductive Node (Data : Type) (n : Nat) : Type where
| empty : Node Data n
| node  (children : Vector (Node Data n) n) : Node Data n
| leaf  (data : Data) : Node Data n

With (children : NArray (Node Data n) n) it produces an error

(kernel) arg #3 of 'Node.node' contains a non valid occurrence of the datatypes being declared

Or another attempt | node (children : Array (Node Data n)) (h : children.size = n) : Node Data n produces

 (kernel) invalid nested inductive datatype 'Eq', nested inductive datatypes parameters cannot contain local variables.

I would much rather deal with Array then with Vector. Also from performance perspective, I'm expecting Array to be faster.

Leonardo de Moura (Apr 02 2022 at 18:46):

@Tomas Skrivan Mario answered this question for Lists here https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.E2.9C.94.20requiring.20proofs.20on.20inductive.20constructors/near/277021847
It is the same thing here but with Array instead of List.

Tomas Skrivan (Apr 02 2022 at 18:48):

I must have completely misunderstand the answer, I will read it again properly.

Leonardo de Moura (Apr 02 2022 at 18:50):

Note that Arrays are defined in Lean as a wrapper around List, and the runtime has special support for their basic operations.
In the future, we can do the same for NArray and Vector.

Tomas Skrivan (Apr 02 2022 at 18:55):

Is there a need for writing special runtime support for NArray? As it is just a pair of Array and a proof, the proof get eliminated in runtime so NArray is effectively just Array. In general, it would be nice if runtime performace of Subtype P, P : α → Prop, is the same as for α.

Leonardo de Moura (Apr 02 2022 at 18:56):

The problem is your Node example above. The NArray defined as a subtype prevents us from using it in a type like Node.

Leonardo de Moura (Apr 02 2022 at 18:59):

If we define NArray as a simple wrapper around Vectorlike Array is a wrapper around List, then Node works, but to get the runtime behavior we want, we need support in the runtime.

Tomas Skrivan (Apr 02 2022 at 19:02):

Right, I think I see the problem. Still do not understand why NArray as subtype is causing problem in the definition of Node, but that is due to my lack of type theory knowledge :grinning:

Leonardo de Moura (Apr 02 2022 at 19:06):

We should probably write documentation for this. This is a recurrent question.

Mario Carneiro (Apr 02 2022 at 19:10):

I believe unsafe inductive is always an option if you really want a certain runtime representation. I think it will allow some crazy dependencies in this case

Mario Carneiro (Apr 02 2022 at 19:12):

it is unfortunate that there is no equivalent of partial for inductives - the fact that unsafe is viral really makes this approach inconvenient to work with

Tomas Skrivan (Apr 02 2022 at 19:15):

I'm considering working with:

inductive Node (Data : Type) : Type where
| empty : Node Data
| node  (children : Array (Node Data)) : Node Data
| leaf  (data : Data) : Node Data

partial def Node.FixedBranching (n : Nat) : Node Data  Prop
| empty => True
| node children => children.size = n   i, (children.get i).FixedBranching n
| leaf _ => True

structure MNode (Data : Type) (m : Nat) where
  node : Node Data
  fix_branching : node.FixedBranching m

Not sure how to show termination though. I should learn the new termination syntax.

Leonardo de Moura (Apr 02 2022 at 19:31):

I often find inductive predicates more convenient to use than definitions such FixedBranching.

inductive Node.FixedBranching (n : Nat) : Node Data  Prop where
  | empty : FixedBranching n empty
  | leaf  : (d : Data)  FixedBranching n (leaf d)
  | node  : {cs : Array (Node Data)}  cs.size = n  ( i, FixedBranching n (cs.get i))  FixedBranching n (node cs)

Tomas Skrivan (Apr 02 2022 at 19:35):

Nice! probably easier to deal with in proofs and does not have a problem with termination.


Last updated: Dec 20 2023 at 11:08 UTC