|
| 1 | +/* |
| 2 | + * SPDX-License-Identifier: MIT |
| 3 | + * Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved. |
| 4 | + */ |
| 5 | + |
| 6 | +use deep_causality::*; |
| 7 | +use std::sync::Arc; |
| 8 | + |
| 9 | +// Contextoid IDs |
| 10 | +const NICOTINE_ID: IdentificationValue = 1; |
| 11 | +const TAR_ID: IdentificationValue = 2; |
| 12 | + |
| 13 | +/// A contextual causal function that determines cancer risk. |
| 14 | +/// It prioritizes checking for tar, then for smoking. |
| 15 | +fn contextual_cancer_risk_logic( |
| 16 | + _effect: &PropagatingEffect, |
| 17 | + context: &Arc<BaseContext>, |
| 18 | +) -> Result<PropagatingEffect, CausalityError> { |
| 19 | + let mut tar_level = 0.0; |
| 20 | + let mut nicotine_level = 0.0; |
| 21 | + |
| 22 | + // Scan the context for relevant data. |
| 23 | + for i in 0..context.number_of_nodes() { |
| 24 | + if let Some(node) = context.get_node(i) { |
| 25 | + if let ContextoidType::Datoid(data_node) = node.vertex_type() { |
| 26 | + match data_node.id() { |
| 27 | + TAR_ID => tar_level = data_node.get_data(), |
| 28 | + NICOTINE_ID => nicotine_level = data_node.get_data(), |
| 29 | + _ => (), |
| 30 | + } |
| 31 | + } |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | + // Causal Logic: High tar is a direct cause of cancer risk, regardless of smoking. |
| 36 | + if tar_level > 0.6 { |
| 37 | + return Ok(PropagatingEffect::Deterministic(true)); |
| 38 | + } |
| 39 | + // If tar is low, then smoking becomes the relevant factor. |
| 40 | + if nicotine_level > 0.6 { |
| 41 | + return Ok(PropagatingEffect::Deterministic(true)); |
| 42 | + } |
| 43 | + |
| 44 | + Ok(PropagatingEffect::Deterministic(false)) |
| 45 | +} |
| 46 | + |
| 47 | +pub fn run_rung3_counterfactual() { |
| 48 | + println!("--- Rung 3: Counterfactual ---"); |
| 49 | + println!("Query: Given a smoker with high tar, what would their cancer risk be if they hadn't smoked?"); |
| 50 | + |
| 51 | + // 1. Define the Causaloid with our contextual logic |
| 52 | + let cancer_risk_causaloid = Causaloid::new_with_context( |
| 53 | + 1, |
| 54 | + contextual_cancer_risk_logic, |
| 55 | + Arc::new(BaseContext::with_capacity(0, "temp", 1)), // Temporary context, will be replaced |
| 56 | + "Contextual Cancer Risk", |
| 57 | + ); |
| 58 | + |
| 59 | + // 2. Create Factual Context: A person who smokes and has high tar. |
| 60 | + let mut factual_context = BaseContext::with_capacity(1, "Factual", 5); |
| 61 | + factual_context.add_node(Contextoid::new(1, ContextoidType::Datoid(Data::new(NICOTINE_ID, 0.8)))).unwrap(); |
| 62 | + factual_context.add_node(Contextoid::new(2, ContextoidType::Datoid(Data::new(TAR_ID, 0.8)))).unwrap(); |
| 63 | + |
| 64 | + // 3. Create Counterfactual Context: Same person, but we hypothetically set smoking to zero. |
| 65 | + let mut counterfactual_context = factual_context.clone(); |
| 66 | + // To update, we need to know the index. In this simple case, it's 0. |
| 67 | + // A real implementation might use a HashMap<ID, Index> for lookup. |
| 68 | + let new_nicotine_datoid = Contextoid::new(1, ContextoidType::Datoid(Data::new(NICOTINE_ID, 0.1))); |
| 69 | + counterfactual_context.update_node(1, new_nicotine_datoid).unwrap(); |
| 70 | + |
| 71 | + // 4. Evaluate Both Scenarios |
| 72 | + let mut factual_causaloid = cancer_risk_causaloid.clone(); |
| 73 | + factual_causaloid.set_context(Some(Arc::new(factual_context))); |
| 74 | + |
| 75 | + let mut counterfactual_causaloid = cancer_risk_causaloid.clone(); |
| 76 | + counterfactual_causaloid.set_context(Some(Arc::new(counterfactual_context))); |
| 77 | + |
| 78 | + let factual_risk = factual_causaloid.evaluate(&PropagatingEffect::None).unwrap(); |
| 79 | + let counterfactual_risk = counterfactual_causaloid.evaluate(&PropagatingEffect::None).unwrap(); |
| 80 | + |
| 81 | + // 5. Assert and Explain |
| 82 | + println!("Factual Result (smoker with high tar): Cancer risk is high -> {}", factual_risk.as_bool().unwrap()); |
| 83 | + println!("Counterfactual Result (non-smoker with high tar): Cancer risk is high -> {}", counterfactual_risk.as_bool().unwrap()); |
| 84 | + |
| 85 | + assert_eq!(factual_risk, PropagatingEffect::Deterministic(true)); |
| 86 | + assert_eq!(counterfactual_risk, PropagatingEffect::Deterministic(true)); |
| 87 | + |
| 88 | + println!("Conclusion: The cancer risk remains high in the counterfactual world because the direct cause (tar) was not undone."); |
| 89 | + println!("\n"); |
| 90 | +} |
0 commit comments