How can I pair up facts to create new facts?

I enjoyed the custom Instruments talks this year and I'm having a stab at creating one myself. I've got a bit stuck when trying to model state changes in a single enumeration. I could probably get this working using os_signpost intervals, but I'd like to know how to accomplish this using CLIPS.


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.


At first glance, this is very similar to the 'ExecutionModeling' example in 'AdvancedModelers'. The difference is that is dealing with two distinct signposts, I've just got a single type with a varying state which I wish to thread together.


Note in the code below, I've omitted the MODELER prefix and used a deftemplate for os-signpost - it's because I've been testing things out using the CLIPS IDE.


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 after a (reset) (run) in CLIPS IDE:


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.

Replies

So it seems that the CLIPS IDE doesn't quite work the same way as Instruments. After a few tweaks I had the following working:


(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 MODELER::update-state
    ?signpost <- (os-signpost
        (subsystem "com.hudl.magician")
        (category "LoggingAVPlayer")
        (name "reasonForWaitingToPlay")
        (event-type "Event")
        (message$ ?state)
        (time ?time)
        (identifier ?identifier)
    )
    (not (state-update (time ?) (state ?)))
    =>
    (retract ?signpost)
    (assert (state-update (time ?time) (state ?state)))
    (log-narrative "update-state %string%" state)
)

(defrule MODELER::pair-state
    ?signpost <- (os-signpost
        (subsystem "com.hudl.magician")
        (category "LoggingAVPlayer")
        (name "reasonForWaitingToPlay")
        (event-type "Event")
        (message$ ?state)
        (time ?time)
        (identifier ?identifier)
    )
    ?update <- (state-update (time ?time1) (state ?state1))
    =>
    (modify ?update (time ?time) (state ?state))
    (retract ?signpost)
    (bind ?duration (- ?time ?time1))
    (assert (state-interval (start ?time1) (duration ?duration) (state ?state1)))
    (log-narrative "Paired between %string% and %string%" ?state1 ?state)
)

(defrule RECORDER::record-reason-interval
    (table (table-id ?output) (side append))
    (table-attribute (table-id ?output) (has schema reason-interval))
    ?interval <- (state-interval (start ?start) (duration ?duration) (state ?state))
    =>
    (create-new-row ?output)
    (set-column start ?start)
    (set-column duration ?duration)
    (set-column state ?state)
    (set-column-narrative "recording for %start-time%, %string%" ?start ?state)
    (retract ?interval)
)


I'm curious as to how I could have better iterated on this in the CLIPS IDE & also if there's a better way I could have done this?

The CLIPS IDE that comes with the open source library is pretty generic, so you'd have to simulate all the missing pieces yourself. Not an easy task. The Instruments Inspector has a modeler console that you can play with to run commands in your modeler, but again, that support is very basic.


Simulating a modeler would be very useful, not just for exploration but also for unit testing modeler code. I encourage you to file a bug requesting that as an enhancement, and then let us know the bug number. Thanks for investing your time in learning this stuff.