-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
import Std.Data
def TypeListToTuple : List Type → Type
| [] => Unit
| [α] => α
| α :: αs => α × TypeListToTuple αs
inductive DTerm (vars : String → Type) (α : Type)
| const (x : α)
| var (s : String) (h : vars s = α := by rfl)
def TypeListToDTerms (vars : String → Type) : List Type → Type
| [] => Unit
| [α] => DTerm vars α
| α :: αs => DTerm vars α × TypeListToDTerms vars αs
def Atom (relations : String → List Type) (vars : String → Type) :=
(rel : String) × TypeListToDTerms vars (relations rel)
def Rule relations vars :=
Atom relations vars × List (Atom relations vars)
def unify
(αs : List Type)
(val : TypeListToTuple αs)
(ds : TypeListToDTerms vars αs)
(env : Std.DHashMap String (vars ·))
(h : ∀ α ∈ αs, BEq α)
: Option (Std.DHashMap String (vars ·)) :=
match αs with
| [] => some env
| [α] => by
have := h α (by grind)
simp [TypeListToTuple] at val
simp [TypeListToDTerms] at ds
match ds with
| .const x =>
exact if val == x then
some env
else
none
| .var s h =>
exact if he : s ∈ env then
if val == h ▸ env.get s he then
some env
else
none
else
some <| env.insert s (h ▸ val)
| α :: α' :: αs => by
have := h α (by grind)
simp [TypeListToTuple] at val
simp [TypeListToDTerms] at ds
match ds.1 with
| .const x =>
exact if val.1 == x then
unify (α' :: αs) val.2 ds.2 env (by solve_by_elim)
else
none
| .var s h =>
exact if he : s ∈ env then
if val.1 == h ▸ env.get s he then
unify (α' :: αs) val.2 ds.2 env (by solve_by_elim)
else
none
else
unify (α' :: αs) val.2 ds.2 (env.insert s (h ▸ val.1)) (by solve_by_elim)
def headToTuple
(rel : String)
(αs : List Type)
(ds : TypeListToDTerms vars αs)
(env : Std.DHashMap String (vars ·))
(hi : ∀ s, Inhabited (vars s))
: TypeListToTuple αs :=
match αs with
| [] => ()
| [α] => by
simp [TypeListToDTerms] at ds
simp [TypeListToTuple]
match ds with
| .const x => exact x
| .var s h => exact h ▸ env.get! s
| α :: α' :: αs => by
simp [TypeListToDTerms] at ds
simp [TypeListToTuple]
let ret := headToTuple rel (α' :: αs) ds.2 env hi
match ds.1 with
| .const x => exact (x, ret)
| .var s h => exact (h ▸ env.get! s, ret)
def naiveEval
(rules : List (Rule relations vars))
(hb : ∀ rel, BEq (TypeListToTuple <| relations rel))
(hh : ∀ rel, Hashable (TypeListToTuple <| relations rel))
(hi : ∀ s, Inhabited (vars s))
(hb' : ∀ rel, ∀ α ∈ relations rel, BEq α)
: Std.DHashMap String (Std.HashSet <| TypeListToTuple <| relations ·) := Id.run do
let RetType := Std.DHashMap String (Std.HashSet <| TypeListToTuple <| relations ·)
let mut cur := .ofList []
repeat
let mut changed := false
let mut nxt := cur
for rule in rules do
let rec unifyRec (env : Std.DHashMap String (vars ·)) (changed : Bool) (cur nxt : RetType) : List (Atom relations vars) → Bool × RetType
| [] =>
let tup := headToTuple rule.1.1 (relations rule.1.1) rule.1.2 env hi
if rule.1.1 ∈ nxt then
if (if h : rule.1.1 ∈ cur then tup ∈ cur.get rule.1.1 h else false) then
(changed, nxt)
else
(true, nxt.modify rule.1.1 (·.insert tup))
else
(true, nxt.insert rule.1.1 (.ofList [tup]))
| atom :: atoms => Id.run do
let mut changed := changed
let mut nxt := nxt
if h : atom.1 ∈ cur then
for val in cur.get atom.1 h do
match unify (relations atom.1) val atom.2 env (hb' atom.1) with
| some env =>
(changed, nxt) := unifyRec env changed cur nxt atoms
| none =>
()
return (changed, nxt)
(changed, nxt) := unifyRec (.ofList []) changed cur nxt rule.2
if !changed then
break
cur := nxt
return cur
def relations : String → List Type
| "edge" | "path" => [Nat, Nat]
| _ => []
def vars : String → Type := fun _ ↦ Nat
def pathRule : Rule relations vars :=
(⟨"path", .var "x", .var "y"⟩, [⟨"edge", .var "x", .var "y"⟩])
def extendPathRule : Rule relations vars :=
(⟨"path", .var "x", .var "z"⟩, [
⟨"path", .var "x", .var "y"⟩,
⟨"edge", .var "y", .var "z"⟩
])
def main := do
let rules := [
pathRule,
extendPathRule,
(⟨"edge", .const 1, .const 2⟩, []),
(⟨"edge", .const 2, .const 3⟩, []),
(⟨"edge", .const 2, .const 4⟩, []),
(⟨"edge", .const 4, .const 1⟩, []),
]
-- The default BEq instance produced by solve_by_elim is bad
have relsBEq (rel : String) : BEq (TypeListToTuple (relations rel)) := by
by_cases h : rel = "edge"
· simpa [h, relations, TypeListToTuple] using instBEqProd
· by_cases h : rel = "path"
· simpa [h, relations, TypeListToTuple] using instBEqProd
· simp [relations, TypeListToTuple]
exact instBEqOfDecidableEq
have relsHashable (rel : String) : Hashable (TypeListToTuple (relations rel)) := by
by_cases h : rel = "edge"
· simpa [h, relations, TypeListToTuple] using instHashableProd
· by_cases h : rel = "path"
· simpa [h, relations, TypeListToTuple] using instHashableProd
· simpa [relations, TypeListToTuple] using instHashablePUnit
have relsBEq' (rel : String) α (h : α ∈ relations rel) : BEq α := by
by_cases hr : rel = "edge"
· simp [hr, relations] at h
simpa [h] using instBEqOfDecidableEq
· by_cases hr : rel = "path"
· simp [hr, relations] at h
simpa [h] using instBEqOfDecidableEq
· simp [relations] at h
let ans := naiveEval rules relsBEq relsHashable (by solve_by_elim) relsBEq'
have : TypeListToTuple [Nat × Nat] = (Nat × Nat) := by rfl
IO.println <| this ▸ (ans.get! "edge" |>.toList)
IO.println <| this ▸ (ans.get! "path" |>.toList)