Friday, March 12, 2010

Reified Semantic Network

Continuing on the path of using classic A.I. tutorial examples to teach myself F#, here is an example using semantic networks. So I created a system in which basic semantic nodes and links can be reified into object instances.

This approach has its pluses and minuses.

On the plus side, F# operator definition makes for a very clean, declarative semantic program. The fact that nodes and links have become instances also makes the code fairly straightforward and efficient.

On the minus side, extending the system at runtime would entail the use of reflection. Also, there is not simple repository for the entities that we can treat as a first class entity, meaning that things like reflection become more difficult and may entail reflection.

So, before going any further and adding things like search, I’ll present this version and then follow up with a version in which entities are represented as data. Then, I’ll pick one and move forward.

Standard disclaimer: presented without warranty or implied suitability of any kind, use at your own risk. And, as always, this is formatted for my narrow blog window, and I present it as a learning exercise, not necessarily either the best way to do F# or the best way to do A.I.
open System.Collections.Generic 


[<AbstractClass>]
type Entity (key:string) =

member this.Key = key



type Link (key:string) =
inherit Entity(key)



type Node (key:string) =
inherit Entity(key)

let links = new Dictionary<Link,HashSet<Node>>()
let props = new Dictionary<Node,HashSet<Node>>()

member private this.TryAdd<'a>
(d:Dictionary<'a,HashSet<Node>>) a =
match d.TryGetValue a with
| (false,_) ->
let h = new HashSet<Node>()
d.Add(a,h)
h
| (true,h) -> h

member private this.TrySub<'a>
(d:Dictionary<'a,HashSet<Node>>) a n =
match d.TryGetValue a with
| (false,_) -> false
| (true,h) ->
let rtn = h.Remove n
if (h.Count=0) then
h.Clear()
d.Remove(a) |> ignore
true

member private this.TryClear<'a>
(d:Dictionary<'a,HashSet<Node>>) a =
match d.TryGetValue a with
| (false,_) -> false
| (true,h) ->
h.Clear()
d.Remove(a) |> ignore
true

member private this.Add ((l,n):Link*Node) =
(this.TryAdd links l).Add n |> ignore
this

member private this.Add ((l:Link),(nl:Node list)) =
match nl with
| [] -> this
| h::t ->
(this.TryAdd links l).Add h |> ignore
this.Add(l,t)

member private this.Add ((n0,n1):Node*Node) =
(this.TryAdd props n0).Add n1 |> ignore
this

member private this.Sub ((l,n):Link*Node) =
this.TrySub links l n |> ignore
this

member private this.Sub ((n0,n1):Node*Node) =
this.TrySub props n0 n1 |> ignore
this

member private this.Sub (l:Link) =
this.TryClear links l |> ignore
this

member private this.Sub (n:Node) =
this.TryClear props n |> ignore
this

static member (+=) ((n:Node),(ln:Link*Node)) =
n.Add ln

static member (+=)
((n:Node),((l,nl):Link*(Node list))) =
n.Add(l,nl)

static member (+=) ((n:Node),(nn:Node*Node)) =
n.Add nn

static member (-=) ((n:Node),(ln:Link*Node)) =
n.Sub ln

static member (-=) ((n:Node),(nn:Node*Node)) =
n.Sub nn

static member (-=) ((n:Node),(l:Link)) =
n.Sub l

static member (-=) ((n0:Node),(n1:Node)) =
n0.Sub n1



let isA = Link "isA"
let hasA = Link "hasA"

let animal = Node "animal"
let canine = Node "dog"
let color = Node "color"
let gray = Node "gray"
let mammal = Node "mammal"
let name = Node "name"
let pet = Node "pet"
let spot = Node "spot"
let tail = Node "tail"
let wine = Node "wine"
let white = Node "white"
let wolfie = Node "wolfie"

white += (isA,[color;name;wine]) |> ignore

white -= (isA,name) -= (isA,wine) |> ignore

gray += (isA,color) |> ignore

mammal += (isA,animal) |> ignore

canine += (isA,mammal) += (hasA,tail) |> ignore

spot
+= (isA,canine)
+= (color,white)
+= (isA,pet) |> ignore

wolfie += (isA,canine) |> ignore
wolfie += (color,gray+=(isA,color)) |> ignore

No comments: