Search code examples
pythonclipsclipspy

Programmatically modify a fact slot using ClipsPy


How to modify a fact of a template in CLIPS using ClipsPy.

I have tried the reassigning slot value and sending modify in clips.build routine, (see py_modify function below) which did not work.

This is the .clp file

;; KB.clp
(deftemplate t
    (slot s_1 (type SYMBOL)))

(defrule main-intent
    (initial-fact)
    =>
    (assert (t (s_1 v_1)))
)

(defrule rule_1
    ?p<-(t (s_1 ?v))
    =>
    (printout t"BEFORE"crlf) (py_pfact)
    (py_modify ?p)
    (printout t"AFTER"crlf) (py_pfact)
)

This is the python file..

# run.py
import clips

clips_env = clips.Environment()

def py_pfact():
    for fact in clips_env.facts():
        print(fact)

def py_modify(p):
    print("--modifying",p["s_1"])
    p["s_1"] = "v_2"  # Try 1
    clips.build("(modify "+str(p.index)+ " (s_1 v_2)") #Try 2

clips_env.define_function(py_pfact)
clips_env.define_function(py_modify)

clips_env.load("KB.clp")
clips_env.reset()
clips_env.run()

The ouput is

 BEFORE
(initial-fact)
(t (s_1 v_1))
--modifying v_1
AFTER
(initial-fact)
(t (s_1 v_1))

I expect s_1 slot to be modified to v_2 from v_1, but it is not.


Solution

  • The environment.build method is for building constructs (defrule, deftemplate, etc.) within the engine. To execute CLIPS code, you need to use environment.eval.

    In CLIPS 6.30 it's not possible to change a fact once asserted (6.40 added APIs for that). Only way to do so is by retracting the old one and asserting a new one with updated values.

    def modify_fact(fact):
        """Modify a template fact."""
        fact.retract()
    
        new_fact = fact.template.new_fact()
        new_fact.update(dict(fact))  # copy over old fact slot values
    
        new_fact["s_1"] = clips.Symbol("v_2") 
    
        new_fact.assertit()
    

    CLIPS provides the modify command which does the exact same: retracts the fact and asserts it with the new value. Nevertheless, it cannot be used via environment.eval as fact indexes cannot be used via the API. If you want to modify a fact within a rule, you'd better use the modify command directly.

    (defrule rule_1
      ?p <- (t (s_1 ?v))
      =>
      (modify ?p (s_1 v_2)))