graphvizrule-engineexpert-systempydotexperta

How to generate a flow chart using the rules engine in python?


I would like to generate a flow using the rules that I defined in the rules engine. Below is the code

from pyknow import *
import pydot

class Wine(Fact):
    """Wine characteristics"""
    pass

class Quality(Fact):
    """Wine quality"""
    pass

class WineExpert(KnowledgeEngine):
    @Rule(Wine(color='red'))
    def rule1(self):
        self.declare(Quality(color='red', quality='rich'))

    @Rule(Wine(color='white'))
    def rule2(self):
        self.declare(Quality(color='white', quality='crisp'))

    @Rule(AND(
        Wine(color='red'),
        Wine(body='light'),
        Wine(tannin='low')
    ))
    def rule3(self):
        self.declare(Quality(color='red', quality='smooth'))

    @Rule(AND(
        Wine(color='red'),
        Wine(body='full'),
        Wine(tannin='high')
    ))
    def rule4(self):
        self.declare(Quality(color='red', quality='bold'))

# Create a new knowledge engine instance
engine = WineExpert()

# Create a Pydot graph
graph = pydot.Dot(graph_type='digraph', comment='Wine Expert Rules')

# Add nodes for each rule
for rule in engine.get_rules():
    node = pydot.Node(rule.__name__)
    graph.add_node(node)

    # Check for AND conditions
    if isinstance(rule.new_conditions, AND):
        for condition in rule.new_conditions.args:
            label = str(condition).replace("<", "\\<").replace(">", "\\>")
            edge = pydot.Edge(label, rule.__name__)
            graph.add_edge(edge)
    else:
        label = str(rule.new_conditions).replace("<", "\\<").replace(">", "\\>")
        edge = pydot.Edge(label, rule.__name__)
        graph.add_edge(edge)

# Save the graph to a file
graph.write_png('wine_expert_rules.png')

I am getting a graph which I can't interpret it. Can anyone tell me how to get a good flowchart out of it?


Solution

  • There seems to be a some confusion with decorators and classes. Also you never execute the rule. Do you expect quality to appear on your flow chart? If so, you should run the rules in your loop. Note: I'm also returning Quality to avoid accessing the engine's agenda and drawing AND nodes (also using experta which is a fork and available at pypi):

    from experta import *
    import pydot
    
    class Wine(Fact):
        """Wine characteristics"""
        pass
    
    class Quality(Fact):
        """Wine quality"""
        pass
    
    class WineExpert(KnowledgeEngine):
        @Rule(Wine(color='red'))
        def rule1(self):
            return self.declare(Quality(color='red', quality='rich'))
    
        @Rule(Wine(color='white'))
        def rule2(self):
            return self.declare(Quality(color='white', quality='crisp'))
    
        @Rule(AND(
            Wine(color='red'),
            Wine(body='light'),
            Wine(tannin='low')
        ))
        def rule3(self):
            return self.declare(Quality(color='red', quality='smooth'))
    
        @Rule(AND(
            Wine(color='red'),
            Wine(body='full'),
            Wine(tannin='high')
        ))
        def rule4(self):
            return self.declare(Quality(color='red', quality='bold'))
    
    # Create a new knowledge engine instance
    engine = WineExpert()
    engine.reset()
    # Create a Pydot graph
    graph = pydot.Dot(graph_type='digraph', comment='Wine Expert Rules')
    
    and_counter = 0
    # Add nodes for each rule
    for rule_instance in engine.get_rules():
        quality = rule_instance()
        qual_node = pydot.Node(repr(quality))
        graph.add_node(qual_node)
        for rule in rule_instance:
            # Check for AND conditions
            if isinstance(rule, AND):
                and_node = pydot.Node(name=and_counter, label='AND')
                graph.add_node(and_node)
                and_counter += 1
                for condition in rule:
                    cond_node = pydot.Node(repr(condition))
                    graph.add_node(cond_node)
                    edge = pydot.Edge(cond_node, and_node)
                    graph.add_edge(edge)
                edge = pydot.Edge(and_node, qual_node)
                graph.add_edge(edge)
            else:
                cond_node = pydot.Node(repr(rule))
                graph.add_node(cond_node)
                edge = pydot.Edge(cond_node, qual_node)
                graph.add_edge(edge)
    
    # Save the graph to a file
    graph.write_png('wine_expert_rules.png')
    

    Output:

    enter image description here