comprehensive_demo/
comprehensive_demo.rs

1//! Comprehensive demonstration of all graph-sp features
2//!
3//! This example shows:
4//! - Tuple-based variable mapping
5//! - Sequential pipelines
6//! - Parallel branching
7//! - Branch merging
8//! - Variant parameter sweeps
9//! - DAG statistics and visualization
10
11use graph_sp::{Graph, Linspace};
12use std::collections::HashMap;
13
14fn main() {
15    println!("═══════════════════════════════════════════════════════════");
16    println!("  Graph-SP Comprehensive Demo");
17    println!("  Pure Rust Graph Execution Engine");
18    println!("═══════════════════════════════════════════════════════════\n");
19    
20    // Run all demonstrations
21    demo_simple_pipeline();
22    demo_branching();
23    demo_merging();
24    demo_variants();
25    demo_complex_graph();
26}
27
28fn demo_simple_pipeline() {
29    println!("─────────────────────────────────────────────────────────");
30    println!("Demo 1: Simple Sequential Pipeline");
31    println!("─────────────────────────────────────────────────────────");
32    
33    let mut graph = Graph::new();
34    
35    // Data source node
36    graph.add(
37        |_: &HashMap<String, String>, _| {
38            let mut result = HashMap::new();
39            result.insert("value".to_string(), "42".to_string());
40            result
41        },
42        Some("DataSource"),
43        None,                                      // No inputs (source node)
44        Some(vec![("value", "data")])              // (impl_var, broadcast_var)
45    );
46    
47    // Processing node: multiply by 2
48    graph.add(
49        |inputs: &HashMap<String, String>, _| {
50            let mut result = HashMap::new();
51            if let Some(val) = inputs.get("x").and_then(|s| s.parse::<i32>().ok()) {
52                result.insert("doubled".to_string(), (val * 2).to_string());
53            }
54            result
55        },
56        Some("Multiply"),
57        Some(vec![("data", "x")]),                 // (broadcast_var, impl_var)
58        Some(vec![("doubled", "result")])          // (impl_var, broadcast_var)
59    );
60    
61    // Final processing: add 10
62    graph.add(
63        |inputs: &HashMap<String, String>, _| {
64            let mut result = HashMap::new();
65            if let Some(val) = inputs.get("num").and_then(|s| s.parse::<i32>().ok()) {
66                result.insert("sum".to_string(), (val + 10).to_string());
67            }
68            result
69        },
70        Some("AddTen"),
71        Some(vec![("result", "num")]),
72        Some(vec![("sum", "final")])
73    );
74    
75    let dag = graph.build();
76    println!("\n📊 Execution:");
77    let context = dag.execute(false, None);
78    
79    println!("  Input:  data = {}", context.get("data").unwrap());
80    println!("  Step 1: result = {} (data * 2)", context.get("result").unwrap());
81    println!("  Step 2: final = {} (result + 10)", context.get("final").unwrap());
82    
83    println!("\n📈 DAG Statistics:");
84    let stats = dag.stats();
85    println!("{}", stats.summary());
86    
87    println!("\n🔍 Mermaid Visualization:");
88    println!("{}", dag.to_mermaid());
89    println!();
90}
91
92fn demo_branching() {
93    println!("─────────────────────────────────────────────────────────");
94    println!("Demo 2: Parallel Branching (Fan-Out)");
95    println!("─────────────────────────────────────────────────────────");
96    
97    let mut graph = Graph::new();
98    
99    // Source node
100    graph.add(
101        |_: &HashMap<String, String>, _| {
102            let mut result = HashMap::new();
103            result.insert("dataset".to_string(), "100".to_string());
104            result
105        },
106        Some("Source"),
107        None,
108        Some(vec![("dataset", "data")])
109    );
110    
111    // Branch A: Compute statistics
112    let mut branch_a = Graph::new();
113    branch_a.add(
114        |inputs: &HashMap<String, String>, _| {
115            let mut result = HashMap::new();
116            if let Some(val) = inputs.get("input") {
117                result.insert("stats".to_string(), format!("Mean: {}", val));
118            }
119            result
120        },
121        Some("Statistics"),
122        Some(vec![("data", "input")]),
123        Some(vec![("stats", "stats_result")])
124    );
125    
126    // Branch B: Train model
127    let mut branch_b = Graph::new();
128    branch_b.add(
129        |inputs: &HashMap<String, String>, _| {
130            let mut result = HashMap::new();
131            if let Some(val) = inputs.get("input") {
132                result.insert("model".to_string(), format!("Model trained on {}", val));
133            }
134            result
135        },
136        Some("MLModel"),
137        Some(vec![("data", "input")]),
138        Some(vec![("model", "model_result")])
139    );
140    
141    // Branch C: Generate visualization
142    let mut branch_c = Graph::new();
143    branch_c.add(
144        |inputs: &HashMap<String, String>, _| {
145            let mut result = HashMap::new();
146            if let Some(val) = inputs.get("input") {
147                result.insert("plot".to_string(), format!("Plot of {}", val));
148            }
149            result
150        },
151        Some("Visualization"),
152        Some(vec![("data", "input")]),
153        Some(vec![("plot", "viz_result")])
154    );
155    
156    graph.branch(branch_a);
157    graph.branch(branch_b);
158    graph.branch(branch_c);
159    
160    let dag = graph.build();
161    println!("\n📊 Execution:");
162    let context = dag.execute(false, None);
163    
164    println!("  Source: data = {}", context.get("data").unwrap());
165    println!("  Branch A (Stats): {}", context.get("stats_result").unwrap());
166    println!("  Branch B (Model): {}", context.get("model_result").unwrap());
167    println!("  Branch C (Viz):   {}", context.get("viz_result").unwrap());
168    
169    println!("\n📈 DAG Statistics:");
170    let stats = dag.stats();
171    println!("{}", stats.summary());
172    println!("  ⚡ All 3 branches can execute in parallel!");
173    
174    println!("\n🔍 Mermaid Visualization:");
175    println!("{}", dag.to_mermaid());
176    println!();
177}
178
179fn demo_merging() {
180    println!("─────────────────────────────────────────────────────────");
181    println!("Demo 3: Branching + Merging (Fan-Out + Fan-In)");
182    println!("─────────────────────────────────────────────────────────");
183    
184    let mut graph = Graph::new();
185    
186    // Source
187    graph.add(
188        |_: &HashMap<String, String>, _| {
189            let mut result = HashMap::new();
190            result.insert("value".to_string(), "50".to_string());
191            result
192        },
193        Some("Source"),
194        None,
195        Some(vec![("value", "data")])
196    );
197    
198    // Branch A: Add 10
199    let mut branch_a = Graph::new();
200    branch_a.add(
201        |inputs: &HashMap<String, String>, _| {
202            let mut result = HashMap::new();
203            if let Some(val) = inputs.get("x").and_then(|s| s.parse::<i32>().ok()) {
204                result.insert("output".to_string(), (val + 10).to_string());
205            }
206            result
207        },
208        Some("PathA (+10)"),
209        Some(vec![("data", "x")]),
210        Some(vec![("output", "result")])  // Both branches use same output name!
211    );
212    
213    // Branch B: Add 20
214    let mut branch_b = Graph::new();
215    branch_b.add(
216        |inputs: &HashMap<String, String>, _| {
217            let mut result = HashMap::new();
218            if let Some(val) = inputs.get("x").and_then(|s| s.parse::<i32>().ok()) {
219                result.insert("output".to_string(), (val + 20).to_string());
220            }
221            result
222        },
223        Some("PathB (+20)"),
224        Some(vec![("data", "x")]),
225        Some(vec![("output", "result")])  // Both branches use same output name!
226    );
227    
228    let branch_a_id = graph.branch(branch_a);
229    let branch_b_id = graph.branch(branch_b);
230    
231    // Merge node: Combine results from both branches
232    graph.merge(
233        |inputs: &HashMap<String, String>, _| {
234            let mut result = HashMap::new();
235            let a = inputs.get("from_a").and_then(|s| s.parse::<i32>().ok()).unwrap_or(0);
236            let b = inputs.get("from_b").and_then(|s| s.parse::<i32>().ok()).unwrap_or(0);
237            result.insert("combined".to_string(), format!("{} + {} = {}", a, b, a + b));
238            result
239        },
240        Some("Merge"),
241        vec![
242            (branch_a_id, "result", "from_a"),  // Map branch A's "result" to merge fn's "from_a"
243            (branch_b_id, "result", "from_b")   // Map branch B's "result" to merge fn's "from_b"
244        ],
245        Some(vec![("combined", "final")])
246    );
247    
248    let dag = graph.build();
249    println!("\n📊 Execution:");
250    let context = dag.execute(false, None);
251    
252    println!("  Source: data = {}", context.get("data").unwrap());
253    println!("  Branch A: 50 + 10 = 60");
254    println!("  Branch B: 50 + 20 = 70");
255    println!("  Merged: {}", context.get("final").unwrap());
256    
257    println!("\n📈 DAG Statistics:");
258    let stats = dag.stats();
259    println!("{}", stats.summary());
260    
261    println!("\n🔍 Mermaid Visualization:");
262    println!("{}", dag.to_mermaid());
263    println!();
264}
265
266fn demo_variants() {
267    println!("─────────────────────────────────────────────────────────");
268    println!("Demo 4: Parameter Sweep with Variants");
269    println!("─────────────────────────────────────────────────────────");
270    
271    let mut graph = Graph::new();
272    
273    // Source node
274    graph.add(
275        |_: &HashMap<String, String>, _| {
276            let mut result = HashMap::new();
277            result.insert("base_value".to_string(), "10.0".to_string());
278            result
279        },
280        Some("DataSource"),
281        None,
282        Some(vec![("base_value", "data")])
283    );
284    
285    // Variant factory: Scale by different learning rates
286    fn make_scaler(learning_rate: f64) -> impl Fn(&HashMap<String, String>, &HashMap<String, String>) -> HashMap<String, String> {
287        move |inputs: &HashMap<String, String>, _| {
288            let mut result = HashMap::new();
289            if let Some(val) = inputs.get("input").and_then(|s| s.parse::<f64>().ok()) {
290                let scaled = val * learning_rate;
291                result.insert("scaled_value".to_string(), format!("{:.2}", scaled));
292            }
293            result
294        }
295    }
296    
297    // Create variants using Linspace for learning rate sweep
298    graph.variant(
299        make_scaler,
300        vec![0.001, 0.01, 0.1, 1.0],
301        Some("ScaleLR"),
302        Some(vec![("data", "input")]),
303        Some(vec![("scaled_value", "result")])
304    );
305    
306    let dag = graph.build();
307    println!("\n📊 Execution:");
308    let context = dag.execute(false, None);
309    
310    println!("  Source: data = {}", context.get("data").unwrap());
311    println!("  Variants created for learning rates: [0.001, 0.01, 0.1, 1.0]");
312    println!("  (Each variant computes: data * learning_rate)");
313    
314    println!("\n📈 DAG Statistics:");
315    let stats = dag.stats();
316    println!("{}", stats.summary());
317    println!("  ⚡ All {} variants can execute in parallel!", stats.variant_count);
318    
319    println!("\n🔍 Mermaid Visualization:");
320    println!("{}", dag.to_mermaid());
321    println!();
322}
323
324fn demo_complex_graph() {
325    println!("─────────────────────────────────────────────────────────");
326    println!("Demo 5: Complex Graph (All Features Combined)");
327    println!("─────────────────────────────────────────────────────────");
328    
329    let mut graph = Graph::new();
330    
331    // 1. Data ingestion
332    graph.add(
333        |_: &HashMap<String, String>, _| {
334            let mut result = HashMap::new();
335            result.insert("raw_data".to_string(), "1000".to_string());
336            result
337        },
338        Some("Ingest"),
339        None,
340        Some(vec![("raw_data", "data")])
341    );
342    
343    // 2. Preprocessing
344    graph.add(
345        |inputs: &HashMap<String, String>, _| {
346            let mut result = HashMap::new();
347            if let Some(val) = inputs.get("raw").and_then(|s| s.parse::<i32>().ok()) {
348                result.insert("cleaned".to_string(), (val / 10).to_string());
349            }
350            result
351        },
352        Some("Preprocess"),
353        Some(vec![("data", "raw")]),
354        Some(vec![("cleaned", "clean_data")])
355    );
356    
357    // 3. Branch for different analyses
358    let mut stats_branch = Graph::new();
359    stats_branch.add(
360        |inputs: &HashMap<String, String>, _| {
361            let mut result = HashMap::new();
362            if let Some(val) = inputs.get("data") {
363                result.insert("stats".to_string(), format!("Stats({})", val));
364            }
365            result
366        },
367        Some("Stats"),
368        Some(vec![("clean_data", "data")]),
369        Some(vec![("stats", "statistics")])
370    );
371    
372    let mut ml_branch = Graph::new();
373    ml_branch.add(
374        |inputs: &HashMap<String, String>, _| {
375            let mut result = HashMap::new();
376            if let Some(val) = inputs.get("data") {
377                result.insert("prediction".to_string(), format!("Pred({})", val));
378            }
379            result
380        },
381        Some("ML"),
382        Some(vec![("clean_data", "data")]),
383        Some(vec![("prediction", "ml_result")])
384    );
385    
386    let stats_id = graph.branch(stats_branch);
387    let ml_id = graph.branch(ml_branch);
388    
389    // 4. Merge branches
390    graph.merge(
391        |inputs: &HashMap<String, String>, _| {
392            let mut result = HashMap::new();
393            let stats = inputs.get("stats_in").cloned().unwrap_or_default();
394            let ml = inputs.get("ml_in").cloned().unwrap_or_default();
395            result.insert("report".to_string(), format!("{} & {}", stats, ml));
396            result
397        },
398        Some("Combine"),
399        vec![
400            (stats_id, "statistics", "stats_in"),
401            (ml_id, "ml_result", "ml_in")
402        ],
403        Some(vec![("report", "final_report")])
404    );
405    
406    // 5. Final output formatting
407    graph.add(
408        |inputs: &HashMap<String, String>, _| {
409            let mut result = HashMap::new();
410            if let Some(report) = inputs.get("report") {
411                result.insert("formatted".to_string(), format!("[FINAL] {}", report));
412            }
413            result
414        },
415        Some("Format"),
416        Some(vec![("final_report", "report")]),
417        Some(vec![("formatted", "output")])
418    );
419    
420    let dag = graph.build();
421    println!("\n📊 Execution:");
422    let context = dag.execute(false, None);
423    
424    println!("  Step 1: Ingest      → data = {}", context.get("data").unwrap());
425    println!("  Step 2: Preprocess  → clean_data = {}", context.get("clean_data").unwrap());
426    println!("  Step 3: Branch A    → statistics = {}", context.get("statistics").unwrap());
427    println!("          Branch B    → ml_result = {}", context.get("ml_result").unwrap());
428    println!("  Step 4: Merge       → final_report = {}", context.get("final_report").unwrap());
429    println!("  Step 5: Format      → output = {}", context.get("output").unwrap());
430    
431    println!("\n📈 DAG Statistics:");
432    let stats = dag.stats();
433    println!("{}", stats.summary());
434    
435    println!("\n📋 Execution Order:");
436    for (level_idx, level) in dag.execution_levels().iter().enumerate() {
437        println!("  Level {}: {} nodes", level_idx, level.len());
438        for &node_id in level {
439            let node = dag.nodes().iter().find(|n| n.id == node_id).unwrap();
440            println!("    - {}", node.display_name());
441        }
442    }
443    
444    println!("\n🔍 Mermaid Visualization:");
445    println!("{}", dag.to_mermaid());
446    println!();
447    
448    println!("═══════════════════════════════════════════════════════════");
449    println!("  Demo Complete!");
450    println!("═══════════════════════════════════════════════════════════");
451}