giovedì 4 dicembre 2014

Frame language: the knowledge based computer aided design (KBCAD) example (part 1)

Create a new project with

lein new app kbcad

Modify the project.clj file so that it contains at least the following dependencies:

[clj-fl "0.1.0-prealfa9"]               ;1
[adamclements/vijual "0.3.0-SNAPSHOT"]] ;2

Where, the first dependency is the Frame language library and the second is Vijual, a graph layout engine.

Start the repl in the project directory with:

lein repl

Create a new namespace called clj-fl-e and "require" the necessary namespaces:

(ns clj-fl-e
  (:require  [clj-fl.core :refer :all]
             [vijual :as v]))

This use case,  inspired to this paper "Constraint-bounded design search", is based on the knowledge base (KB) stored in the following gist:

Loading ....

To feed the FL system with the previous KB you can download the gist and load it at th REPL with the following function:

(load-kb-vec "filename")

Or you can load the raw gist in one shot giving the address of the raw gist to the load-kb-vec function:

(load-kb-vec "https://gist.githubusercontent.com/capitancook/da8ff741afa4734dd7b9/raw/d05a4c4db676c295cfc3ed405448373342e4b3c0/kb-kbcad1.clj")

At the beginning of this KB we have the definition of the metaclasses or "ancestor" frames:

;------ AKO frames

{:frame       {:value "bu"}
 :name        {:value "Building Unit"}
 :description {:value "A man-made structure with floors, roofs and walls standing more or

                       less permanently in one place. Can be comprised of other building unit
                       and/or space units"}} 

{:frame       {:value "su"}
 :name        {:value "Space Unit"}
 :description {:value "A space unit is a building unit component. Is composed of building 
                       elements"}}

{:frame       {:value "be"}
 :name        {:value "Building Element"}
 :description {:value "BEs are the physical elements that define the BU and SU. For example,
                      a wall is a BE, a pillar is a BE and so on"}}

Here, a generic building has been modelled with the frame "bu", a building unit, that can be composed of other building unit and/or  "su",space unit, frames.
A "be", building element is any physical element that compose a space unit. For example a perimetral wall, a floor and a pillar are all "be".
Then, we have the definition of the isa frames:

;------ ISA frames

{:frame       {:value "flat"}
 :name        {:value "Flat"}
 :description {:value "A flat"}
 :ako         {:value "bu"}}

{:frame       {:value "room"}
 :name        {:value "Room"}
 :description {:value "A room"}
 :ako         {:value "su"}}

{:frame       {:value "sw"}
 :name        {:value "Structural Wall"}
 :description {:value "A structural wall is schematized as a line thicker than a partition 
                      wall"}
 :ako         {:value "be"}}
{:frame       {:value "pw"}
 :name        {:value "Partition Wall"}
 :description {:value "A partition wall is schematized as a line thinner than a structural 
                      wall"}
 :ako         {:value "be"}}


Here we can see that a "flat" is a "bu", while a "room" is a "su" and a structural wall "sw" and a partition wall "pw" are "be".
Then we have the instances of the isa frames where we can see that our "flat" "flat1" is comprised by the frames "kitchen1", "bathroom1" and "bedroom1". These frames are listed in the :cof, comprised-of, slot of the "flat1" frame.

{:frame {:value "flat1"}
 :name  {:value "Flat 1"}
 :isa   {:value "flat"}
 :cof   {:value ("kitchen1" "bedroom1" "bathroom1")}}


Each space unit is described in terms of the walls that surround the unit and in terms of the building unit that composes.
For example "bedroom1" is comprised of  5 walls ("w5" "w4" "w8" "w7" "w6"), the values of the :cof slot, and compose the "flat1" building unit frame contained in the facet :value of the :isi slot

{:frame {:value "bedroom1"}
 :name  {:value "Bedroom 1"}
 :isa   {:value "room"}
 :cof   {:value ("w5" "w4" "w8" "w7" "w6")}

 :isi   {:value "flat1"}}

The walls are the building elements that physically realise the space units of our knowledge base. There are two kind of walls: perimetral and partition walls. The first is usually thicker and stronger that the latter.
Each wall is defined in terms of the space unit that composes, the :isi slot, and the two snap points, the :sps slot, that define the beginning and the end points of the wall.
The tree diagram of our knowledge base is the following:

Fig. 1 - Tree graph of the KB

At the REPL you can, for example,  query the KB asking what are the snap points of the wall "w1" with the following function:

clj-fl-examples>(fget "w1" :sps :value)
((0 5) (0 0))

But, if you ask the description slot of the same frame you obtain nil. We can try to see if the slot "w1" inherited a slot :description from is :isa or ancestor frame using the following function:

clj-fl-examples>(fget-i "w1" :description :value)
"A structural wall is schematized as a line thicker than a partition wall"

Since "w1" is a "sw" frame,  the system returned the :description slot of the "sw", structural wall, frame.

Let's add some spices to this example.

FL supports the use of stored function (SF).  They are normal Clojure functions that can exploit all the power of the FL and can be activated both directly that indirectly. The former are stored function that can be invoked directly by the user of the KB while the latter are indirectly activated when certains triggers are fired. A simple function showcofs that extracts the tree graph of  the :cof relation for a given frame f and that can be stored in the :shocofs slot of f or in a f's meaframe, is the following:

Loading ....

In 1 the list of the :cof frames is stored in cofslist. In 2, if cofslist is empty, i.e. there are no comprised-of frames of f, f is returned in a vector. In 3, when cofslist is not empty,  step number 2 is "reduced" accumulating the results in a vector of vector. Note that in the generic reducing step, showproc is initialized to (fget-ii currentframe :showcofs :proc). This means that we can have different function stored in different metaframe and that the user doesn't need to know about the function showcofs. In fact, the user can invoke the extraction of the tree graph using:

((eval (fget-ii "flat1" :showcofs :proc)) "flat1")

To test what we have done up to this point you can copy&paste the showcofs function from the above box to the REPL and load the KB kb-kbcad2.clj that contains a modified version of kb-kbcad1.clj with the addition of a :showcofs slot in the metaframes "bu", "su" and "be". To load the new KB copy&paste the following function at the REPL:

(load-kb-vec "https://gist.githubusercontent.com/capitancook/ec3839d32b4d4d33b636/raw/b384ffe5c7e9b781923d3ff87f6bd53152966a5a/kb-kbcad2.clj")

Now, if you type at the REPL

((eval (fget-ii "flat1" :showcofs :proc)) "flat1")

you'll obtain the following representation of the graph tree of the :cof relation in our KB:

["flat1" ["kitchen1" ["w1"] ["w2"] ["w3"] ["w4"]] ["bedroom1" ["w5"] ["w4"] ["w8"] ["w7"] ["w6"]] ["bathroom1" ["w3"] ["w10"] ["w9"] ["w8"]]]

To have more information about this particular format used to represent graphs by means of vectors structure, please go to the lisperati web site.
Anyway, pretty-printing the last result you can easily recognize the tree structure of the result:

(clojure.pprint/pprint *1)
["flat1"
 ["kitchen1" ["w1"] ["w2"] ["w3"] ["w4"]]
 ["bedroom1" ["w5"] ["w4"] ["w8"] ["w7"] ["w6"]]
 ["bathroom1" ["w3"] ["w10"] ["w9"] ["w8"]]]

And you can see a cute ascii art layout of the graph tree typing the following function:

(v/draw-tree *1)

We have already seen the result of this function in the above  Fig.1.

Ascii art is cute but a CAD should be able to graphically render a design like the one in Fig. 2

Fig. 2 - 2d rendering of the "bu" frame "flat1"
Graphic rendering and more on FL stored procedure in the next post.

Hope you enjoyed today's post!Thanks for reading.

Nessun commento:

Posta un commento