Class: KBS::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/kbs/engine.rb

Direct Known Subclasses

Blackboard::Engine

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEngine

Returns a new instance of Engine.



7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/kbs/engine.rb', line 7

def initialize
  @working_memory = WorkingMemory.new
  @rules = []
  @alpha_memories = {}
  @production_nodes = {}
  @root_beta_memory = BetaMemory.new

  # Add initial dummy token to root beta memory
  # This represents "no conditions matched yet" and allows the first condition to match
  @root_beta_memory.add_token(Token.new(nil, nil, nil))

  @working_memory.add_observer(self)
end

Instance Attribute Details

#alpha_memoriesObject (readonly)

Returns the value of attribute alpha_memories.



5
6
7
# File 'lib/kbs/engine.rb', line 5

def alpha_memories
  @alpha_memories
end

#production_nodesObject (readonly)

Returns the value of attribute production_nodes.



5
6
7
# File 'lib/kbs/engine.rb', line 5

def production_nodes
  @production_nodes
end

#rulesObject (readonly)

Returns the value of attribute rules.



5
6
7
# File 'lib/kbs/engine.rb', line 5

def rules
  @rules
end

#working_memoryObject (readonly)

Returns the value of attribute working_memory.



5
6
7
# File 'lib/kbs/engine.rb', line 5

def working_memory
  @working_memory
end

Instance Method Details

#add_fact(type, attributes = {}) ⇒ Object



26
27
28
29
30
# File 'lib/kbs/engine.rb', line 26

def add_fact(type, attributes = {})
  fact = Fact.new(type, attributes)
  @working_memory.add_fact(fact)
  fact
end

#add_rule(rule) ⇒ Object



21
22
23
24
# File 'lib/kbs/engine.rb', line 21

def add_rule(rule)
  @rules << rule
  build_network_for_rule(rule)
end

#remove_fact(fact) ⇒ Object



32
33
34
# File 'lib/kbs/engine.rb', line 32

def remove_fact(fact)
  @working_memory.remove_fact(fact)
end

#resetObject

Clear all transient RETE state while preserving the compiled rule network.

The RETE network has four levels of transient state:

1. WorkingMemory  holds all asserted facts
2. AlphaMemory    holds facts matching each pattern
3. BetaMemory     holds tokens from condition joins
4. ProductionNode  holds tokens ready to fire

Simply clearing working memory facts doesn't cascade reliably through intermediate beta memories. Stale tokens in beta memories cause false matches when new facts are asserted on the next cycle.

This method clears all four levels directly while preserving the root beta memory's dummy token (needed for first-condition joins).



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/kbs/engine.rb', line 70

def reset
  # 1. Clear working memory directly (bypass observer — we clear everything)
  @working_memory.facts.clear

  # 2. Clear alpha memories and their downstream beta memories
  @alpha_memories.each_value do |alpha_mem|
    alpha_mem.items.clear

    # Each alpha memory successor is a JoinNode or NegationNode.
    # Their successors are intermediate BetaMemory nodes.
    # The root beta memory is never a join node successor, so
    # its dummy token is preserved.
    alpha_mem.successors.each do |join_node|
      next unless join_node.respond_to?(:successors)
      join_node.successors.each do |beta_or_prod|
        beta_or_prod.tokens.clear if beta_or_prod.respond_to?(:tokens)
      end
    end
  end

  # 3. Clear production node tokens
  @production_nodes.each_value { |node| node.tokens.clear }

  # 4. Clear stale child references from the root dummy token
  @root_beta_memory.tokens.each { |t| t.children.clear }
end

#runObject



48
49
50
51
52
53
54
# File 'lib/kbs/engine.rb', line 48

def run
  @production_nodes.values.each do |node|
    node.tokens.each do |token|
      node.fire_rule(token)
    end
  end
end

#update(action, fact) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/kbs/engine.rb', line 36

def update(action, fact)
  if action == :add
    @alpha_memories.each do |pattern, memory|
      memory.activate(fact) if fact.matches?(pattern)
    end
  elsif action == :remove
    @alpha_memories.each do |pattern, memory|
      memory.deactivate(fact) if fact.matches?(pattern)
    end
  end
end