Skip to main content

02_scirs2_execution/
main.rs

1//! Example demonstrating end-to-end logic compilation and execution with SciRS2 backend.
2//!
3//! This example shows:
4//! 1. Defining a logic rule (Parent → Ancestor)
5//! 2. Compiling to einsum graph
6//! 3. Creating input tensors with SciRS2
7//! 4. Executing the graph
8//! 5. Inspecting outputs
9
10use tensorlogic_compiler::compile_to_einsum;
11use tensorlogic_infer::{TlAutodiff, TlExecutor};
12use tensorlogic_ir::{TLExpr, Term};
13use tensorlogic_scirs_backend::Scirs2Exec;
14
15fn main() -> anyhow::Result<()> {
16    println!("=== Tensorlogic + SciRS2: End-to-End Execution ===\n");
17
18    // 1. Define logic rule: Parent(x, y) → Ancestor(x, y)
19    let parent = TLExpr::pred("Parent", vec![Term::var("x"), Term::var("y")]);
20    let ancestor = TLExpr::pred("Ancestor", vec![Term::var("x"), Term::var("y")]);
21    let rule = TLExpr::imply(parent.clone(), ancestor.clone());
22
23    println!("Logic Rule: Parent(x, y) → Ancestor(x, y)");
24    println!("Interpretation: If Parent(x,y) holds, then Ancestor(x,y) should hold\n");
25
26    // 2. Compile to einsum graph
27    println!("Compiling to tensor graph...");
28    let graph = compile_to_einsum(&rule)?;
29    println!("  Tensors: {}", graph.tensors.len());
30    println!("  Nodes: {}", graph.nodes.len());
31
32    for (idx, tensor_name) in graph.tensors.iter().enumerate() {
33        println!("  Tensor {}: {}", idx, tensor_name);
34    }
35
36    for (idx, node) in graph.nodes.iter().enumerate() {
37        println!("  Node {}: {:?} (inputs: {:?})", idx, node.op, node.inputs);
38    }
39    println!();
40
41    // 3. Create SciRS2 executor and input tensors
42    println!("Creating SciRS2 executor...");
43    let mut executor = Scirs2Exec::new();
44
45    // Create a 3x3 Parent relation tensor (3 people, 3 people)
46    // Parent[i,j] = 1.0 if person i is parent of person j
47    let parent_data = vec![
48        0.0, 1.0, 1.0, // Person 0 is parent of 1, 2
49        0.0, 0.0, 0.0, // Person 1 has no children
50        0.0, 0.0, 0.0, // Person 2 has no children
51    ];
52
53    let parent_tensor = Scirs2Exec::from_vec(parent_data, vec![3, 3])?;
54    println!("Parent tensor shape: {:?}", parent_tensor.shape());
55    println!("Parent tensor:\n{:?}\n", parent_tensor);
56
57    // Create expected Ancestor tensor (also 3x3, same as Parent for this simple case)
58    let ancestor_data = vec![
59        0.0, 1.0, 1.0, // Person 0 is ancestor of 1, 2
60        0.0, 0.0, 0.0, // Person 1 has no descendants
61        0.0, 0.0, 0.0, // Person 2 has no descendants
62    ];
63
64    let ancestor_tensor = Scirs2Exec::from_vec(ancestor_data, vec![3, 3])?;
65    println!(
66        "Expected Ancestor tensor shape: {:?}",
67        ancestor_tensor.shape()
68    );
69    println!("Expected Ancestor tensor:\n{:?}\n", ancestor_tensor);
70
71    // Add tensors to executor storage using dynamic names from compiled graph
72    executor.add_tensor(graph.tensors[0].clone(), parent_tensor);
73    executor.add_tensor(graph.tensors[1].clone(), ancestor_tensor);
74
75    // 4. Execute the graph
76    println!("Executing forward pass...");
77    let output = executor.forward(&graph)?;
78    println!("Output tensor shape: {:?}", output.shape());
79    println!("Output tensor (constraint violation):\n{:?}\n", output);
80
81    // 5. Interpret results
82    println!("=== Interpretation ===");
83    println!("The output tensor represents constraint violations (via ReLU(Ancestor - Parent)).");
84    println!("  - Values near 0: rule satisfied (Parent implies Ancestor)");
85    println!("  - Positive values: violations (Parent holds but Ancestor doesn't)\n");
86
87    // Example 2: Test element-wise operations
88    println!("=== Testing SciRS2 Element-wise Operations ===\n");
89
90    let test_data = vec![-2.0, -1.0, 0.0, 1.0, 2.0];
91    let test_tensor = Scirs2Exec::from_vec(test_data, vec![5])?;
92
93    println!("Input: {:?}", test_tensor);
94
95    let relu_result = executor.elem_op(tensorlogic_infer::ElemOp::Relu, &test_tensor)?;
96    println!("ReLU:  {:?}", relu_result);
97
98    let sigmoid_result = executor.elem_op(tensorlogic_infer::ElemOp::Sigmoid, &test_tensor)?;
99    println!("Sigmoid: {:?}", sigmoid_result);
100
101    let one_minus_result = executor.elem_op(tensorlogic_infer::ElemOp::OneMinus, &test_tensor)?;
102    println!("OneMinus: {:?}\n", one_minus_result);
103
104    // Example 3: Test reduction operations
105    println!("=== Testing SciRS2 Reduction Operations ===\n");
106
107    let matrix_data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
108    let matrix = Scirs2Exec::from_vec(matrix_data, vec![2, 3])?;
109
110    println!("Input matrix (2x3):\n{:?}\n", matrix);
111
112    let sum_axis0 = executor.reduce(tensorlogic_infer::ReduceOp::Sum, &matrix, &[0])?;
113    println!("Sum over axis 0: {:?}", sum_axis0);
114
115    let sum_axis1 = executor.reduce(tensorlogic_infer::ReduceOp::Sum, &matrix, &[1])?;
116    println!("Sum over axis 1: {:?}", sum_axis1);
117
118    let max_axis0 = executor.reduce(tensorlogic_infer::ReduceOp::Max, &matrix, &[0])?;
119    println!("Max over axis 0: {:?}", max_axis0);
120
121    let mean_axis1 = executor.reduce(tensorlogic_infer::ReduceOp::Mean, &matrix, &[1])?;
122    println!("Mean over axis 1: {:?}\n", mean_axis1);
123
124    println!("=== Execution Complete ===");
125    println!("\nThis demonstrates the full pipeline:");
126    println!("  Logic → IR → Einsum Graph → SciRS2 Execution → Results");
127
128    Ok(())
129}