Search code examples
xcodeinstrumentsclips

How can I pair up facts to create new facts?


I'm new to CLIPS and am trying to use it to create a custom Instrument in Xcode to model state changes in an enumeration.

I want to end up with a series of facts which represent the start and end time of the enumeration being in a given state.

I'm emitting an event signpost each time there is a state change within my code, this can be simplified to look like:

(deftemplate signpost
    (slot time (type INTEGER))
    (slot state (type STRING))
)

I'm trying to pair them up to generate state interval facts which look like:

(deftemplate state-interval
    (slot start (type INTEGER))
    (slot duration (type INTEGER))
    (slot state (type STRING))
)

Given the input data:

(signpost (time 0) (state "one"))
(signpost (time 2) (state "two"))
(signpost (time 5) (state "three"))
(signpost (time 10) (state "four"))

I'd expect to see the following output:

(state-interval (start 0) (duration 2) (state "one"))
(state-interval (start 2) (duration 3) (state "two"))
(state-interval (start 5) (duration 5) (state "three"))

My closest attempt so far looks like:

(deftemplate signpost
    (slot time (type INTEGER))
    (slot state (type STRING))
)

(deftemplate state-update
    (slot time (type INTEGER))
    (slot state (type STRING))
)

(deftemplate state-interval
    (slot start (type INTEGER))
    (slot duration (type INTEGER))
    (slot state (type STRING))
)

(defrule update-state
    ?signpost <- (signpost (time ?time) (state ?state))
    (not (state-update (time ?) (state ?)))
    =>
    (retract ?signpost)
    (assert (state-update (time ?time) (state ?state)))
)

(defrule pair-state
    ?signpost <- (signpost (time ?time) (state ?state))
    ?update <- (state-update (time ?time1) (state ?state1))
    =>
    (modify ?update (time ?time) (state ?state))
    (retract ?signpost)
    (bind ?duration (- ?time1 ?time))
    (assert (state-interval (start ?time1) (duration ?duration) (state ?state1)))
)

(deffacts sample-data
    (signpost (time 0) (state "one"))
    (signpost (time 2) (state "two"))
    (signpost (time 5) (state "three"))
    (signpost (time 10) (state "four"))
)

I get the following output:

FIRE    1 update-state: f-4,*
<== f-4     (signpost (time 10) (state "four"))
==> f-5     (state-update (time 10) (state "four"))
==> Activation 0      pair-state: f-3,f-5
==> Activation 0      pair-state: f-2,f-5
==> Activation 0      pair-state: f-1,f-5
<== Activation 0      update-state: f-3,*
<== Activation 0      update-state: f-2,*
<== Activation 0      update-state: f-1,*
FIRE    2 pair-state: f-1,f-5
<== f-5     (state-update (time 10) (state "four"))
<== Activation 0      pair-state: f-2,f-5
<== Activation 0      pair-state: f-3,f-5
==> Activation 0      update-state: f-1,*
==> Activation 0      update-state: f-2,*
==> Activation 0      update-state: f-3,*
==> f-6     (state-update (time 0) (state "one"))
==> Activation 0      pair-state: f-3,f-6
==> Activation 0      pair-state: f-2,f-6
==> Activation 0      pair-state: f-1,f-6
<== Activation 0      update-state: f-3,*
<== Activation 0      update-state: f-2,*
<== Activation 0      update-state: f-1,*
<== f-1     (signpost (time 0) (state "one"))
<== Activation 0      pair-state: f-1,f-6
==> f-7     (state-interval (start 10) (duration 10) (state "four"))
FIRE    3 pair-state: f-2,f-6
<== f-6     (state-update (time 0) (state "one"))
<== Activation 0      pair-state: f-3,f-6
==> Activation 0      update-state: f-2,*
==> Activation 0      update-state: f-3,*
==> f-8     (state-update (time 2) (state "two"))
==> Activation 0      pair-state: f-3,f-8
==> Activation 0      pair-state: f-2,f-8
<== Activation 0      update-state: f-3,*
<== Activation 0      update-state: f-2,*
<== f-2     (signpost (time 2) (state "two"))
<== Activation 0      pair-state: f-2,f-8
==> f-9     (state-interval (start 0) (duration -2) (state "one"))
FIRE    4 pair-state: f-3,f-8
<== f-8     (state-update (time 2) (state "two"))
==> Activation 0      update-state: f-3,*
==> f-10    (state-update (time 5) (state "three"))
==> Activation 0      pair-state: f-3,f-10
<== Activation 0      update-state: f-3,*
<== f-3     (signpost (time 5) (state "three"))
<== Activation 0      pair-state: f-3,f-10
==> f-11    (state-interval (start 2) (duration -3) (state "two"))
<== Focus MAIN
4 rules fired        Run time is 0.00531400000909343 seconds.
752.728640036717 rules per second.
5 mean number of facts (5 maximum).
1 mean number of instances (1 maximum).
2 mean number of activations (4 maximum).

Resulting in:

CLIPS> (facts)
f-0     (initial-fact)
f-7     (state-interval (start 10) (duration 10) (state "four"))
f-9     (state-interval (start 0) (duration -2) (state "one"))
f-10    (state-update (time 5) (state "three"))
f-11    (state-interval (start 2) (duration -3) (state "two"))
For a total of 5 facts.

I feel like I'm missing something fundamental here. In my mind I'm assuming that Xcode is going to be feeding me in these signpost facts sequentially over time.


Solution

  •          CLIPS (6.31 4/1/19)
    CLIPS> 
    (deftemplate signpost
        (slot time (type INTEGER))
        (slot state (type STRING)))
    CLIPS> 
    (deftemplate state-interval
        (slot start (type INTEGER))
        (slot duration (type INTEGER))
        (slot state (type STRING)))
    CLIPS> 
    (defrule find-interval
    
       ;; Find the first signpost
    
       (signpost (time ?time1) (state ?state))
    
       ;; And a second signpost that comes later
    
       (signpost (time ?time2&:(> ?time2 ?time1)))
    
       ;; And there's no other signpost between the two
    
       (not (signpost (time ?time3&:(> ?time3 ?time1)
                                  &:(< ?time3 ?time2))))
       =>
    
       (assert (state-interval (start ?time1)
                               (duration (- ?time2 ?time1))
                               (state ?state))))
    CLIPS>    
    (deffacts sample-data
        (signpost (time 0) (state "one"))
        (signpost (time 2) (state "two"))
        (signpost (time 5) (state "three"))
        (signpost (time 10) (state "four")))
    CLIPS> (watch rules)
    CLIPS> (watch facts)
    CLIPS> (watch activations)
    CLIPS> (reset)
    <== f-0     (initial-fact)
    ==> f-0     (initial-fact)
    ==> f-1     (signpost (time 0) (state "one"))
    ==> f-2     (signpost (time 2) (state "two"))
    ==> Activation 0      find-interval: f-1,f-2,*
    ==> f-3     (signpost (time 5) (state "three"))
    ==> Activation 0      find-interval: f-2,f-3,*
    ==> f-4     (signpost (time 10) (state "four"))
    ==> Activation 0      find-interval: f-3,f-4,*
    CLIPS> (run)
    FIRE    1 find-interval: f-3,f-4,*
    ==> f-5     (state-interval (start 5) (duration 5) (state "three"))
    FIRE    2 find-interval: f-2,f-3,*
    ==> f-6     (state-interval (start 2) (duration 3) (state "two"))
    FIRE    3 find-interval: f-1,f-2,*
    ==> f-7     (state-interval (start 0) (duration 2) (state "one"))
    CLIPS> (facts)
    f-0     (initial-fact)
    f-1     (signpost (time 0) (state "one"))
    f-2     (signpost (time 2) (state "two"))
    f-3     (signpost (time 5) (state "three"))
    f-4     (signpost (time 10) (state "four"))
    f-5     (state-interval (start 5) (duration 5) (state "three"))
    f-6     (state-interval (start 2) (duration 3) (state "two"))
    f-7     (state-interval (start 0) (duration 2) (state "one"))
    For a total of 8 facts.
    CLIPS>