kbs 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +52 -0
  3. data/CHANGELOG.md +68 -2
  4. data/README.md +235 -334
  5. data/docs/DOCUMENTATION_STATUS.md +158 -0
  6. data/docs/advanced/custom-persistence.md +775 -0
  7. data/docs/advanced/debugging.md +726 -0
  8. data/docs/advanced/index.md +8 -0
  9. data/docs/advanced/performance.md +832 -0
  10. data/docs/advanced/testing.md +691 -0
  11. data/docs/api/blackboard.md +1157 -0
  12. data/docs/api/engine.md +978 -0
  13. data/docs/api/facts.md +1212 -0
  14. data/docs/api/index.md +12 -0
  15. data/docs/api/rules.md +1034 -0
  16. data/docs/architecture/blackboard.md +553 -0
  17. data/docs/architecture/index.md +277 -0
  18. data/docs/architecture/network-structure.md +343 -0
  19. data/docs/architecture/rete-algorithm.md +737 -0
  20. data/docs/assets/css/custom.css +83 -0
  21. data/docs/assets/images/blackboard-architecture.svg +136 -0
  22. data/docs/assets/images/compiled-network.svg +101 -0
  23. data/docs/assets/images/fact-assertion-flow.svg +117 -0
  24. data/docs/assets/images/kbs.jpg +0 -0
  25. data/docs/assets/images/pattern-matching-trace.svg +136 -0
  26. data/docs/assets/images/rete-network-layers.svg +96 -0
  27. data/docs/assets/images/system-layers.svg +69 -0
  28. data/docs/assets/images/trading-signal-network.svg +139 -0
  29. data/docs/assets/js/mathjax.js +17 -0
  30. data/docs/examples/expert-systems.md +1031 -0
  31. data/docs/examples/index.md +9 -0
  32. data/docs/examples/multi-agent.md +1335 -0
  33. data/docs/examples/stock-trading.md +488 -0
  34. data/docs/guides/blackboard-memory.md +558 -0
  35. data/docs/guides/dsl.md +1321 -0
  36. data/docs/guides/facts.md +652 -0
  37. data/docs/guides/getting-started.md +383 -0
  38. data/docs/guides/index.md +23 -0
  39. data/docs/guides/negation.md +529 -0
  40. data/docs/guides/pattern-matching.md +561 -0
  41. data/docs/guides/persistence.md +451 -0
  42. data/docs/guides/variable-binding.md +491 -0
  43. data/docs/guides/writing-rules.md +755 -0
  44. data/docs/index.md +157 -0
  45. data/docs/installation.md +156 -0
  46. data/docs/quick-start.md +228 -0
  47. data/examples/README.md +2 -2
  48. data/examples/advanced_example.rb +2 -2
  49. data/examples/advanced_example_dsl.rb +224 -0
  50. data/examples/ai_enhanced_kbs.rb +1 -1
  51. data/examples/ai_enhanced_kbs_dsl.rb +538 -0
  52. data/examples/blackboard_demo_dsl.rb +50 -0
  53. data/examples/car_diagnostic.rb +1 -1
  54. data/examples/car_diagnostic_dsl.rb +54 -0
  55. data/examples/concurrent_inference_demo.rb +5 -5
  56. data/examples/concurrent_inference_demo_dsl.rb +363 -0
  57. data/examples/csv_trading_system.rb +1 -1
  58. data/examples/csv_trading_system_dsl.rb +525 -0
  59. data/examples/knowledge_base.db +0 -0
  60. data/examples/portfolio_rebalancing_system.rb +2 -2
  61. data/examples/portfolio_rebalancing_system_dsl.rb +613 -0
  62. data/examples/redis_trading_demo_dsl.rb +177 -0
  63. data/examples/run_all.rb +50 -0
  64. data/examples/run_all_dsl.rb +49 -0
  65. data/examples/stock_trading_advanced.rb +1 -1
  66. data/examples/stock_trading_advanced_dsl.rb +404 -0
  67. data/examples/temp.txt +7693 -0
  68. data/examples/temp_dsl.txt +8447 -0
  69. data/examples/timestamped_trading.rb +1 -1
  70. data/examples/timestamped_trading_dsl.rb +258 -0
  71. data/examples/trading_demo.rb +1 -1
  72. data/examples/trading_demo_dsl.rb +322 -0
  73. data/examples/working_demo.rb +1 -1
  74. data/examples/working_demo_dsl.rb +160 -0
  75. data/lib/kbs/blackboard/engine.rb +3 -3
  76. data/lib/kbs/blackboard/fact.rb +1 -1
  77. data/lib/kbs/condition.rb +1 -1
  78. data/lib/kbs/dsl/knowledge_base.rb +1 -1
  79. data/lib/kbs/dsl/variable.rb +1 -1
  80. data/lib/kbs/{rete_engine.rb → engine.rb} +1 -1
  81. data/lib/kbs/fact.rb +1 -1
  82. data/lib/kbs/version.rb +1 -1
  83. data/lib/kbs.rb +2 -2
  84. data/mkdocs.yml +181 -0
  85. metadata +66 -6
  86. data/examples/stock_trading_system.rb.bak +0 -563
@@ -0,0 +1,491 @@
1
+ # Variable Binding
2
+
3
+ Variables connect facts across conditions, enabling join constraints in the RETE network. This guide explains how binding works, join tests, and optimization strategies.
4
+
5
+ ## Variable Syntax
6
+
7
+ Variables start with `?` and are symbols:
8
+
9
+ ```ruby
10
+ :temp? # Variable named "temp"
11
+ :sensor_id? # Variable named "sensor_id"
12
+ :x? # Variable named "x"
13
+ ```
14
+
15
+ **Naming conventions:**
16
+ - Use lowercase with underscores
17
+ - Be descriptive
18
+ - Match domain terminology
19
+
20
+ ## Basic Binding
21
+
22
+ ### Single Variable
23
+
24
+ ```ruby
25
+ KBS::Condition.new(:sensor, { temp: :t? })
26
+
27
+ # Matches fact:
28
+ { type: :sensor, temp: 28 }
29
+
30
+ # Creates binding:
31
+ { :t? => 28 }
32
+ ```
33
+
34
+ ### Multiple Variables
35
+
36
+ ```ruby
37
+ KBS::Condition.new(:stock, {
38
+ symbol: :sym?,
39
+ price: :price?,
40
+ volume: :vol?
41
+ })
42
+
43
+ # Matches:
44
+ { type: :stock, symbol: "AAPL", price: 150, volume: 1000 }
45
+
46
+ # Bindings:
47
+ {
48
+ :sym? => "AAPL",
49
+ :price? => 150,
50
+ :vol? => 1000
51
+ }
52
+ ```
53
+
54
+ ## Cross-Condition Binding
55
+
56
+ ### Join Constraints
57
+
58
+ Variables with the same name create equality constraints:
59
+
60
+ ```ruby
61
+ r.conditions = [
62
+ # Condition 1: Binds :id? to sensor's id
63
+ KBS::Condition.new(:sensor, { id: :id?, temp: :temp? }),
64
+
65
+ # Condition 2: Must match same :id?
66
+ KBS::Condition.new(:threshold, { sensor_id: :id?, max: :max? })
67
+ ]
68
+ ```
69
+
70
+ **Join test:**
71
+ ```
72
+ sensor[:id] == threshold[:sensor_id]
73
+ ```
74
+
75
+ ### Multiple Joins
76
+
77
+ ```ruby
78
+ r.conditions = [
79
+ KBS::Condition.new(:a, { x: :v1?, y: :v2? }),
80
+ KBS::Condition.new(:b, { p: :v1?, q: :v3? }),
81
+ KBS::Condition.new(:c, { m: :v2?, n: :v3? })
82
+ ]
83
+
84
+ # Join tests:
85
+ # a[:x] == b[:p] (via :v1?)
86
+ # a[:y] == c[:m] (via :v2?)
87
+ # b[:q] == c[:n] (via :v3?)
88
+ ```
89
+
90
+ ### Visual Example
91
+
92
+ ```
93
+ Condition 1: stock(symbol: :sym?, price: :p?)
94
+ Condition 2: watchlist(symbol: :sym?)
95
+ Condition 3: alert_config(symbol: :sym?, threshold: :t?)
96
+
97
+ Variable :sym? creates two joins:
98
+ ├─ stock[:symbol] == watchlist[:symbol]
99
+ └─ stock[:symbol] == alert_config[:symbol]
100
+ ```
101
+
102
+ ## Binding Lifecycle
103
+
104
+ ### 1. First Occurrence: Bind
105
+
106
+ ```ruby
107
+ # First condition with :sym?
108
+ KBS::Condition.new(:stock, { symbol: :sym? })
109
+
110
+ # Fact matches
111
+ { type: :stock, symbol: "AAPL" }
112
+
113
+ # Binding created:
114
+ { :sym? => "AAPL" }
115
+ ```
116
+
117
+ ### 2. Subsequent Occurrences: Test
118
+
119
+ ```ruby
120
+ # Second condition with :sym?
121
+ KBS::Condition.new(:watchlist, { symbol: :sym? })
122
+
123
+ # Checks if symbol == "AAPL" (from previous binding)
124
+ # Matches:
125
+ { type: :watchlist, symbol: "AAPL" } # ✓
126
+
127
+ # Does not match:
128
+ { type: :watchlist, symbol: "GOOGL" } # ✗
129
+ ```
130
+
131
+ ### 3. Action: Access
132
+
133
+ ```ruby
134
+ r.action = lambda do |facts, bindings|
135
+ # Access bound variables
136
+ symbol = bindings[:sym?]
137
+ price = bindings[:p?]
138
+
139
+ puts "#{symbol} at $#{price}"
140
+ end
141
+ ```
142
+
143
+ ## Join Tests
144
+
145
+ ### What is a Join Test?
146
+
147
+ A join test verifies that variable values match across facts:
148
+
149
+ ```ruby
150
+ r.conditions = [
151
+ KBS::Condition.new(:a, { x: :v? }),
152
+ KBS::Condition.new(:b, { y: :v? })
153
+ ]
154
+
155
+ # Join test structure:
156
+ {
157
+ token_field_index: 0, # Index of fact in token (first condition)
158
+ token_field: :x, # Attribute name in first fact
159
+ fact_field: :y, # Attribute name in new fact
160
+ operation: :eq # Equality test
161
+ }
162
+ ```
163
+
164
+ ### Join Test Execution
165
+
166
+ ```ruby
167
+ def perform_join_test(token, new_fact, test)
168
+ # Get value from token (previous facts)
169
+ token_fact = token.facts[test[:token_field_index]]
170
+ token_value = token_fact[test[:token_field]]
171
+
172
+ # Get value from new fact
173
+ fact_value = new_fact[test[:fact_field]]
174
+
175
+ # Test equality
176
+ token_value == fact_value
177
+ end
178
+ ```
179
+
180
+ ### Example Execution
181
+
182
+ ```ruby
183
+ # Rule
184
+ r.conditions = [
185
+ KBS::Condition.new(:sensor, { id: :id?, temp: :temp? }),
186
+ KBS::Condition.new(:threshold, { sensor_id: :id?, max: :max? })
187
+ ]
188
+
189
+ # Facts
190
+ sensor = { type: :sensor, id: "bedroom", temp: 28 }
191
+ threshold = { type: :threshold, sensor_id: "bedroom", max: 25 }
192
+
193
+ # Join execution:
194
+ # 1. sensor matches → token created
195
+ token = Token.new(parent: root, fact: sensor)
196
+ # Bindings: { :id? => "bedroom", :temp? => 28 }
197
+
198
+ # 2. threshold tested against token
199
+ test = {
200
+ token_field_index: 0, # sensor is first fact
201
+ token_field: :id, # sensor's id attribute
202
+ fact_field: :sensor_id, # threshold's sensor_id attribute
203
+ operation: :eq
204
+ }
205
+
206
+ # 3. Perform join
207
+ token_value = sensor[:id] # "bedroom"
208
+ fact_value = threshold[:sensor_id] # "bedroom"
209
+ result = token_value == fact_value # true
210
+ # ✓ Join succeeds → new token created
211
+ ```
212
+
213
+ ## Binding Strategies
214
+
215
+ ### Pattern 1: Primary Key Join
216
+
217
+ Connect facts via identifier:
218
+
219
+ ```ruby
220
+ r.conditions = [
221
+ KBS::Condition.new(:order, {
222
+ id: :order_id?,
223
+ status: "pending"
224
+ }),
225
+
226
+ KBS::Condition.new(:payment, {
227
+ order_id: :order_id?,
228
+ verified: true
229
+ })
230
+ ]
231
+
232
+ # Matches orders with verified payments
233
+ ```
234
+
235
+ ### Pattern 2: Multi-Attribute Join
236
+
237
+ Join on multiple fields:
238
+
239
+ ```ruby
240
+ r.conditions = [
241
+ KBS::Condition.new(:trade, {
242
+ symbol: :sym?,
243
+ date: :date?,
244
+ volume: :vol?
245
+ }),
246
+
247
+ KBS::Condition.new(:settlement, {
248
+ symbol: :sym?,
249
+ trade_date: :date?
250
+ })
251
+ ]
252
+
253
+ # Joins on both symbol AND date
254
+ ```
255
+
256
+ ### Pattern 3: Transitive Binding
257
+
258
+ Chain bindings across three+ conditions:
259
+
260
+ ```ruby
261
+ r.conditions = [
262
+ KBS::Condition.new(:a, { id: :x? }),
263
+ KBS::Condition.new(:b, { a_id: :x?, id: :y? }),
264
+ KBS::Condition.new(:c, { b_id: :y? })
265
+ ]
266
+
267
+ # a[:id] == b[:a_id]
268
+ # b[:id] == c[:b_id]
269
+ # Creates chain: a → b → c
270
+ ```
271
+
272
+ ### Pattern 4: Fan-Out Join
273
+
274
+ One fact joins with multiple:
275
+
276
+ ```ruby
277
+ r.conditions = [
278
+ KBS::Condition.new(:sensor, { id: :id?, temp: :t? }),
279
+ KBS::Condition.new(:threshold, { sensor_id: :id? }),
280
+ KBS::Condition.new(:alert_config, { sensor_id: :id? }),
281
+ KBS::Condition.new(:location, { sensor_id: :id? })
282
+ ]
283
+
284
+ # All join on :id?
285
+ ```
286
+
287
+ ## Performance Implications
288
+
289
+ ### Join Cardinality
290
+
291
+ ```ruby
292
+ # Condition 1: 100 sensor facts
293
+ KBS::Condition.new(:sensor, { temp: :t? })
294
+
295
+ # Condition 2: 200 threshold facts
296
+ KBS::Condition.new(:threshold, { max: :m? })
297
+
298
+ # Without variable binding:
299
+ # Potential matches: 100 × 200 = 20,000
300
+
301
+ # With variable binding:
302
+ KBS::Condition.new(:sensor, { id: :id?, temp: :t? })
303
+ KBS::Condition.new(:threshold, { sensor_id: :id?, max: :m? })
304
+
305
+ # Actual matches: ~100 (1:1 relationship)
306
+ ```
307
+
308
+ **Variable bindings dramatically reduce join size.**
309
+
310
+ ### Beta Memory Size
311
+
312
+ ```ruby
313
+ # Bad: No shared variables
314
+ r.conditions = [
315
+ KBS::Condition.new(:a, {}), # 1000 facts
316
+ KBS::Condition.new(:b, {}), # 1000 facts
317
+ KBS::Condition.new(:c, {}) # 1000 facts
318
+ ]
319
+ # Beta memory: 1000 × 1000 × 1000 = 1,000,000,000 tokens!
320
+
321
+ # Good: Shared variables
322
+ r.conditions = [
323
+ KBS::Condition.new(:a, { id: :id? }),
324
+ KBS::Condition.new(:b, { a_id: :id? }),
325
+ KBS::Condition.new(:c, { a_id: :id? })
326
+ ]
327
+ # Beta memory: ~1000 tokens (assuming 1:1:1 relationship)
328
+ ```
329
+
330
+ ### Optimization Tips
331
+
332
+ **1. Use specific bindings:**
333
+
334
+ ```ruby
335
+ # Good: Binds sensor to specific readings
336
+ KBS::Condition.new(:sensor, { id: :id? })
337
+ KBS::Condition.new(:reading, { sensor_id: :id? })
338
+
339
+ # Bad: No binding (cross product)
340
+ KBS::Condition.new(:sensor, {})
341
+ KBS::Condition.new(:reading, {})
342
+ ```
343
+
344
+ **2. Order by selectivity:**
345
+
346
+ ```ruby
347
+ # Good: Specific first
348
+ r.conditions = [
349
+ KBS::Condition.new(:critical_alert, { id: :id? }), # 1 fact
350
+ KBS::Condition.new(:sensor, { id: :id? }) # 1000 facts
351
+ ]
352
+ # Beta memory: 1 token
353
+
354
+ # Bad: General first
355
+ r.conditions = [
356
+ KBS::Condition.new(:sensor, { id: :id? }), # 1000 facts
357
+ KBS::Condition.new(:critical_alert, { id: :id? }) # 1 fact
358
+ ]
359
+ # Beta memory: 1000 tokens
360
+ ```
361
+
362
+ **3. Minimize cross products:**
363
+
364
+ ```ruby
365
+ # Bad: No shared variables between first two conditions
366
+ r.conditions = [
367
+ KBS::Condition.new(:a, { x: :v1? }),
368
+ KBS::Condition.new(:b, { y: :v2? }), # No :v1?!
369
+ KBS::Condition.new(:c, { p: :v1?, q: :v2? })
370
+ ]
371
+ # Creates a × b cross product
372
+
373
+ # Good: Progressive joining
374
+ r.conditions = [
375
+ KBS::Condition.new(:a, { x: :v1? }),
376
+ KBS::Condition.new(:c, { p: :v1?, q: :v2? }),
377
+ KBS::Condition.new(:b, { y: :v2? })
378
+ ]
379
+ # Each condition reduces search space
380
+ ```
381
+
382
+ ## Common Patterns
383
+
384
+ ### One-to-Many Relationship
385
+
386
+ ```ruby
387
+ # One customer, many orders
388
+ r.conditions = [
389
+ KBS::Condition.new(:customer, {
390
+ id: :cust_id?,
391
+ status: "active"
392
+ }),
393
+
394
+ KBS::Condition.new(:order, {
395
+ customer_id: :cust_id?,
396
+ status: "pending"
397
+ })
398
+ ]
399
+
400
+ # Fires once per pending order for active customers
401
+ ```
402
+
403
+ ### Many-to-Many Relationship
404
+
405
+ ```ruby
406
+ # Students enrolled in courses
407
+ r.conditions = [
408
+ KBS::Condition.new(:student, { id: :student_id? }),
409
+ KBS::Condition.new(:enrollment, {
410
+ student_id: :student_id?,
411
+ course_id: :course_id?
412
+ }),
413
+ KBS::Condition.new(:course, { id: :course_id? })
414
+ ]
415
+
416
+ # Fires for each student-course pair
417
+ ```
418
+
419
+ ### Hierarchical Join
420
+
421
+ ```ruby
422
+ # Parent → Child → Grandchild
423
+ r.conditions = [
424
+ KBS::Condition.new(:category, { id: :cat_id? }),
425
+ KBS::Condition.new(:product, {
426
+ category_id: :cat_id?,
427
+ id: :prod_id?
428
+ }),
429
+ KBS::Condition.new(:review, {
430
+ product_id: :prod_id?,
431
+ rating: :rating?
432
+ })
433
+ ]
434
+ ```
435
+
436
+ ## Debugging Bindings
437
+
438
+ ### Print Bindings
439
+
440
+ ```ruby
441
+ r.action = lambda do |facts, bindings|
442
+ puts "Bindings: #{bindings.inspect}"
443
+ puts "Facts:"
444
+ facts.each_with_index do |fact, i|
445
+ puts " #{i}: #{fact.type} #{fact.attributes}"
446
+ end
447
+ end
448
+ ```
449
+
450
+ ### Trace Join Tests
451
+
452
+ ```ruby
453
+ class DebugJoinNode < KBS::JoinNode
454
+ def perform_join_tests(token, fact)
455
+ result = super
456
+ puts "Join test: #{@tests.inspect}"
457
+ puts " Token: #{token.inspect}"
458
+ puts " Fact: #{fact.inspect}"
459
+ puts " Result: #{result}"
460
+ result
461
+ end
462
+ end
463
+ ```
464
+
465
+ ### Validate Bindings
466
+
467
+ ```ruby
468
+ r.action = lambda do |facts, bindings|
469
+ # Ensure expected bindings exist
470
+ required = [:sensor_id?, :temp?, :max?]
471
+ missing = required - bindings.keys
472
+
473
+ if missing.any?
474
+ raise "Missing bindings: #{missing}"
475
+ end
476
+
477
+ # Proceed with action
478
+ # ...
479
+ end
480
+ ```
481
+
482
+ ## Next Steps
483
+
484
+ - **[Pattern Matching](pattern-matching.md)** - How facts match conditions
485
+ - **[Negation](negation.md)** - Negated conditions and binding
486
+ - **[Network Structure](../architecture/network-structure.md)** - How joins compile into networks
487
+ - **[Performance Guide](../advanced/performance.md)** - Optimizing join performance
488
+
489
+ ---
490
+
491
+ *Variable binding is the glue that connects facts. Master bindings, master rule performance.*