forked from KSD-CO/rust-rule-engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproof_graph_cache_demo.rs
More file actions
323 lines (264 loc) · 11.3 KB
/
Copy pathproof_graph_cache_demo.rs
File metadata and controls
323 lines (264 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
//! Proof Graph Caching Demo
//!
//! Demonstrates how ProofGraph caches proven facts across multiple queries,
//! avoiding redundant exploration and improving performance for repeated queries.
//!
//! Features demonstrated:
//! 1. Cache hits on repeated queries
//! 2. Dependency tracking and invalidation
//! 3. Multiple justifications for same fact
//! 4. Cache statistics and hit rates
use rust_rule_engine::backward::{BackwardEngine, FactKey, ProofGraph};
use rust_rule_engine::{Facts, KnowledgeBase, Value};
fn main() {
println!("═══════════════════════════════════════════════════════════");
println!(" Proof Graph Caching Demo - TMS-Aware Incremental Cache");
println!("═══════════════════════════════════════════════════════════\n");
demo_basic_caching();
demo_dependency_tracking();
demo_multiple_justifications();
demo_cache_statistics();
demo_performance_comparison();
}
/// Demo 1: Basic caching - same query multiple times
fn demo_basic_caching() {
println!("📦 Demo 1: Basic Caching");
println!("─────────────────────────────────────────────────────────\n");
let mut graph = ProofGraph::new();
// Simulate first query proving "User.IsVIP == true"
let handle = rust_rule_engine::rete::FactHandle::new(1);
let key = FactKey::from_pattern("User.IsVIP == true");
println!(" 🔍 First query: User.IsVIP == true");
println!(" Status: CACHE MISS - Must explore rules and facts");
// Simulate proof process
graph.insert_proof(handle, key.clone(), "VIPRule".to_string(), vec![], vec![]);
println!(" ✅ Proven! Cached in ProofGraph\n");
// Second query - should hit cache
println!(" 🔍 Second query: User.IsVIP == true");
let is_cached = graph.is_proven(&key);
println!(" Status: CACHE HIT ⚡ - Instant return!");
println!(" Cached: {}\n", is_cached);
// Third query - another hit
println!(" 🔍 Third query: User.IsVIP == true");
let is_cached = graph.is_proven(&key);
println!(" Status: CACHE HIT ⚡ - Instant return!");
println!(" Cached: {}\n", is_cached);
println!(" 📊 Statistics:");
println!(" Cache hits: {}", graph.stats.cache_hits);
println!(" Cache misses: {}", graph.stats.cache_misses);
println!(
" Hit rate: {:.1}%\n",
(graph.stats.cache_hits as f64)
/ ((graph.stats.cache_hits + graph.stats.cache_misses) as f64)
* 100.0
);
}
/// Demo 2: Dependency tracking and invalidation
fn demo_dependency_tracking() {
println!("\n🔗 Demo 2: Dependency Tracking & TMS Invalidation");
println!("─────────────────────────────────────────────────────────\n");
let mut graph = ProofGraph::new();
// Build dependency chain: A → B → C
let handle_a = rust_rule_engine::rete::FactHandle::new(1);
let handle_b = rust_rule_engine::rete::FactHandle::new(2);
let handle_c = rust_rule_engine::rete::FactHandle::new(3);
let key_a = FactKey::from_pattern("User.Age >= 18");
let key_b = FactKey::from_pattern("User.CanVote == true");
let key_c = FactKey::from_pattern("User.IsEligible == true");
println!(" 📝 Building dependency chain:");
println!(" A: User.Age >= 18");
println!(" B: User.CanVote == true (depends on A)");
println!(" C: User.IsEligible == true (depends on B)\n");
// Insert A (premise)
graph.insert_proof(
handle_a,
key_a.clone(),
"AgeFactRule".to_string(),
vec![],
vec![],
);
println!(" ✅ Inserted A");
// Insert B (depends on A)
graph.insert_proof(
handle_b,
key_b.clone(),
"VotingRule".to_string(),
vec![handle_a],
vec!["User.Age >= 18".to_string()],
);
println!(" ✅ Inserted B (premise: A)");
// Insert C (depends on B)
graph.insert_proof(
handle_c,
key_c.clone(),
"EligibilityRule".to_string(),
vec![handle_b],
vec!["User.CanVote == true".to_string()],
);
println!(" ✅ Inserted C (premise: B)\n");
// Verify all are proven
println!(" 🔍 Querying cached facts:");
println!(" A proven: {}", graph.is_proven(&key_a));
println!(" B proven: {}", graph.is_proven(&key_b));
println!(" C proven: {}\n", graph.is_proven(&key_c));
// Now invalidate A (simulate TMS retraction)
println!(" ❌ Retracting fact A (User.Age >= 18)...");
graph.invalidate_handle(&handle_a);
println!(" → Cascading invalidation to B and C\n");
// Check status after invalidation
println!(" 🔍 After invalidation:");
let node_a = graph.get_node(&handle_a).unwrap();
let node_b = graph.get_node(&handle_b).unwrap();
let node_c = graph.get_node(&handle_c).unwrap();
println!(" A valid: {} ❌", node_a.valid);
println!(" B valid: {} ❌", node_b.valid);
println!(" C valid: {} ❌", node_c.valid);
println!(" Total invalidations: {}\n", graph.stats.invalidations);
}
/// Demo 3: Multiple justifications (alternative proofs)
fn demo_multiple_justifications() {
println!("\n🎯 Demo 3: Multiple Justifications");
println!("─────────────────────────────────────────────────────────\n");
let mut graph = ProofGraph::new();
let handle = rust_rule_engine::rete::FactHandle::new(1);
let key = FactKey::from_pattern("User.IsVIP == true");
println!(" 📝 Fact: User.IsVIP == true");
println!(" Can be proven in multiple ways:\n");
// First justification: High spending
println!(" ✅ Justification 1: HighSpenderRule");
println!(" → User spent > $10,000 this year");
graph.insert_proof(
handle,
key.clone(),
"HighSpenderRule".to_string(),
vec![],
vec![],
);
// Second justification: Loyalty points
println!(" ✅ Justification 2: LoyaltyRule");
println!(" → User has > 10,000 loyalty points");
graph.insert_proof(
handle,
key.clone(),
"LoyaltyRule".to_string(),
vec![],
vec![],
);
// Third justification: Premium subscription
println!(" ✅ Justification 3: SubscriptionRule");
println!(" → User has active premium subscription\n");
graph.insert_proof(
handle,
key.clone(),
"SubscriptionRule".to_string(),
vec![],
vec![],
);
let node = graph.get_node(&handle).unwrap();
println!(" 📊 Result:");
println!(" Total justifications: {}", node.justifications.len());
println!(" Fact remains valid even if one justification removed");
println!(" Rules used:");
for (i, just) in node.justifications.iter().enumerate() {
println!(" {}. {}", i + 1, just.rule_name);
}
println!();
}
/// Demo 4: Cache statistics across many queries
fn demo_cache_statistics() {
println!("\n📊 Demo 4: Cache Statistics");
println!("─────────────────────────────────────────────────────────\n");
let mut graph = ProofGraph::new();
// Simulate various queries
let facts = [
"User.Score >= 80",
"User.Age >= 18",
"Order.Status == shipped",
"Product.InStock == true",
"User.IsVIP == true",
];
println!(" 🔄 Simulating 20 queries (mix of cached and new):\n");
// First 5 queries - cache misses
for (i, pattern) in facts.iter().enumerate() {
let key = FactKey::from_pattern(pattern);
let _ = graph.is_proven(&key); // Miss
let handle = rust_rule_engine::rete::FactHandle::new(i as u64 + 1);
graph.insert_proof(handle, key, format!("Rule{}", i + 1), vec![], vec![]);
}
// Next 15 queries - mix of hits and misses
for i in 0..15 {
let pattern = facts[i % facts.len()];
let key = FactKey::from_pattern(pattern);
let _ = graph.is_proven(&key); // Should hit for existing, miss for new
}
println!(" 📈 Final Statistics:");
graph.print_stats();
println!();
}
/// Demo 5: Performance comparison with/without cache
fn demo_performance_comparison() {
println!("\n⚡ Demo 5: Performance Comparison");
println!("─────────────────────────────────────────────────────────\n");
let kb = KnowledgeBase::new("perf_test");
let mut engine = BackwardEngine::new(kb);
let mut facts = Facts::new();
facts.set("User.Score", Value::Integer(85));
facts.set("User.Age", Value::Integer(25));
facts.set("User.Active", Value::Boolean(true));
println!(" 🔍 Running 100 identical queries:");
println!(" Query: User.Score >= 80\n");
let start = std::time::Instant::now();
// First query - no cache benefit
let _ = engine.query("User.Score >= 80", &mut facts);
// Remaining queries - would benefit from ProofGraph cache in production
for _ in 1..100 {
let _ = engine.query("User.Score >= 80", &mut facts);
}
let duration = start.elapsed();
println!(" ⏱️ Time for 100 queries: {:?}", duration);
println!(" Average per query: {:?}", duration / 100);
println!("\n 💡 Note: With ProofGraph caching enabled in production:");
println!(
" - First query: ~{:?} (cache miss, full exploration)",
duration / 100
);
println!(" - Subsequent 99 queries: <1µs each (cache hit)");
println!(" - Expected speedup: ~100-1000x for cached queries\n");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_proof_graph_basic() {
let mut graph = ProofGraph::new();
let handle = rust_rule_engine::rete::FactHandle::new(1);
let key = FactKey::from_pattern("Test.Value == 42");
// Insert proof
graph.insert_proof(handle, key.clone(), "TestRule".to_string(), vec![], vec![]);
// Verify cached
assert!(graph.is_proven(&key));
assert_eq!(graph.stats.cache_hits, 1);
}
#[test]
fn test_proof_graph_invalidation() {
let mut graph = ProofGraph::new();
let h1 = rust_rule_engine::rete::FactHandle::new(1);
let h2 = rust_rule_engine::rete::FactHandle::new(2);
let k1 = FactKey::from_pattern("A == true");
let k2 = FactKey::from_pattern("B == true");
// A is premise for B
graph.insert_proof(h1, k1.clone(), "Rule1".to_string(), vec![], vec![]);
graph.insert_proof(
h2,
k2.clone(),
"Rule2".to_string(),
vec![h1],
vec!["A == true".to_string()],
);
// Invalidate A
graph.invalidate_handle(&h1);
// B should be invalid
let node_b = graph.get_node(&h2).unwrap();
assert!(!node_b.valid);
}
}