Zulip Chat Archive

Stream: Is there code for X?

Topic: Attribute Domains


Paul Rowe (Oct 16 2022 at 22:12):

I'm having trouble figuring out how to go about formalizing a concept called "attribute domains". The basic idea is that you have some inductive type α. This type has some list of constructors of various arities. We want to define a function f : α → vals into some (typically ordered) type of values. This function f should "match up" the constructors of α with some other list of operators on vals, where the value of f is recursively defined according to the inductive structure of the elements of α.

So far, there's no problem. If I know α and I have a list of operators in mind, I can just write the recursive function. But I'm looking for a general way of telling Lean how to build the recursive function based on, say, the type α and the list of operators to use for the respective cases.

Here's an example based on the specific structures I'm looking to support.

inductive attack_tree (lab : Type)
| atom : lab  attack_tree
| or : attack_tree  attack_tree  attack_tree
| and : attack_tree  attack_tree  attack_tree
| seq : attack_tree  attack_tree  attack_tree
open attack_tree

inductive place
| pl : string  place

inductive program
| pr : string  program

inductive component : Type
| c : place  program  component

inductive asp
| msp : component  component  asp
| nul : asp
| cpy : asp
| hsh : asp
| sig : asp

inductive dir
| pos : dir
| neg : dir

inductive split
| mk : dir  dir  split

inductive copland
| atom : asp  copland
| lseq : copland  copland  copland
| bseq : split  copland  copland  copland
| bpar : split  copland  copland  copland
| att : place  copland  copland
open copland

variable {lab : Type}

def AD1 (V : attack_tree lab  ) : attack_tree lab  
| (atom a) := V (atom a)
| (or t1 t2) := min (AD1 t1) (AD1 t2)
| (and t1 t2) := AD1 t1 + AD1 t2
| (seq t1 t2) := max (AD1 t1) (AD1 t2)

def AD2 (V : copland  ) : copland  
| (atom a) := V (atom a)
| (bpar s c1 c2) := AD2 c1 + AD2 c2
| (lseq c1 c2) := max (AD2 c1) (AD2 c2)
| (bseq s c1 c2) := max (AD2 c1) (AD2 c2)
| (att q c1) := AD2 c1

-- Input types are clearly wrong. How to package the necessary info?
def AD_general (α : Type) (vals : Type) (ops : list Type) :
 α  vals := sorry

I'd like the general construction to prove things like, "as long as attack_tree.and gets associated with the same operator as copland.bpar and attack_tree.seq gets associated with the same operator as copland.lseq and copland.bseq, then a property holds about the AD1 version iff it holds about the AD2 version".

Matt Diamond (Oct 17 2022 at 00:39):

I wonder if docs#W_type might be relevant here, as it represents inductively defined trees

Paul Rowe (Oct 17 2022 at 02:04):

Thanks. I’ll check into that lead when I get a chance. I really appreciate the suggestion!

Paul Rowe (Oct 17 2022 at 16:30):

While I have a feeling using W_types would be helpful, I decided to go with a more modest solution. Since I have two particular inductive types in mind, I can at least parameterize each of those with a list (vector, really) of functions. Since I know the number of constructors and their arities, I can hard code those into the lists. Just for reference in case it helps anybody, a revised version is below that shows the equality of the new approach with the one-off solutions.

import data.vector tactic

inductive attack_tree (lab : Type)
| atom : lab  attack_tree
| or : attack_tree  attack_tree  attack_tree
| and : attack_tree  attack_tree  attack_tree
| seq : attack_tree  attack_tree  attack_tree
open attack_tree

inductive place
| pl : string  place

inductive program
| pr : string  program

inductive component : Type
| c : place  program  component

inductive asp
| msp : component  component  asp
| nul : asp
| cpy : asp
| hsh : asp
| sig : asp

inductive dir
| pos : dir
| neg : dir

inductive split
| mk : dir  dir  split

inductive copland
| atom : asp  copland
| lseq : copland  copland  copland
| bseq : split  copland  copland  copland
| bpar : split  copland  copland  copland
| att : place  copland  copland
open copland

variable {lab : Type}

def AD1 (V : attack_tree lab  ) : attack_tree lab  
| (atom a) := V (atom a)
| (or t1 t2) := min (AD1 t1) (AD1 t2)
| (and t1 t2) := AD1 t1 + AD1 t2
| (seq t1 t2) := max (AD1 t1) (AD1 t2)


-- An alternative that takes a list of functions as an argument.
def AD1' (V : attack_tree lab  ) (l2 : vector (    ) 3): attack_tree lab  
| (atom a) := V (atom a)
| (or t1 t2) := (vector.nth l2 0) (AD1' t1) (AD1' t2)
| (and t1 t2) := (vector.nth l2 1) (AD1' t1) (AD1' t2)
| (seq t1 t2) := (vector.nth l2 2) (AD1' t1) (AD1' t2)

def ad1ops : vector (    ) 3 :=
⟨[(λ a b, min a b), (λ a b, a + b), (λ a b, max a b)], by simp

lemma AD1_eq (V : attack_tree lab  ) (t : attack_tree lab) :
AD1 V t = AD1' V ad1ops t :=
begin
  induction t,
  case attack_tree.atom : t
  { simp [AD1, AD1'], },
  case attack_tree.or : t1 t2 IH1 IH2
  { simp [AD1, AD1'],
    rw [IH1, IH2], refl, },
  case attack_tree.and : t1 t2 IH1 IH2
  { simp [AD1, AD1'],
    rw [IH1, IH2], refl, },
  case attack_tree.seq : t1 t2 IH1 IH2
  { simp [AD1, AD1'],
    rw [IH1, IH2], refl, }
end

def AD2 (V : copland  ) : copland  
| (atom a) := V (atom a)
| (bpar s c1 c2) := AD2 c1 + AD2 c2
| (lseq c1 c2) := max (AD2 c1) (AD2 c2)
| (bseq s c1 c2) := max (AD2 c1) (AD2 c2)
| (att q c1) := AD2 c1

def AD2' (V : copland  ) (l1 :   ) (l2 : vector (    ) 2) : copland  
| (atom a) := V (atom a)
| (bpar s c1 c2) := (vector.nth l2 0) (AD2' c1) (AD2' c2)
| (lseq c1 c2) := (vector.nth l2 1) (AD2' c1) (AD2' c2)
| (bseq s c1 c2) := (vector.nth l2 1) (AD2' c1) (AD2' c2)
| (att q c1) := l1 (AD2' c1)

def ad2ops : vector (    ) 2 :=
⟨[λ a b, a + b, λ a b, max a b], by simp

lemma AD2_eq (V : copland  ) (c : copland) :
AD2 V c = AD2' V id ad2ops c :=
begin
  induction c,
  case copland.atom : c
  { simp [AD2, AD2'], },
  case copland.lseq : c1 c2 IH1 IH2
  { simp [AD2, AD2', IH1, IH2], refl, },
  case copland.bseq : s c1 c2 IH1 IH2
  { simp [AD2, AD2', IH1, IH2], refl, },
  case copland.bpar : s c1 c2 IH1 IH2
  { simp [AD2, AD2', IH1, IH2], refl, },
  case copland.att : q c1 IH
  { simp [AD2, AD2', IH], },
end

Last updated: Dec 20 2023 at 11:08 UTC