use super::*;
use std::collections::HashMap;
fn create_credit_risk_network() -> BayesianConfig {
BayesianConfig::new("Credit Risk")
.with_node(
"economic_conditions",
BayesianNode::discrete(vec!["good", "neutral", "bad"]).with_prior(vec![0.3, 0.5, 0.2]),
)
.with_node(
"company_revenue",
BayesianNode::discrete(vec!["high", "medium", "low"])
.with_parents(vec!["economic_conditions"])
.with_cpt_entry("good", vec![0.6, 0.3, 0.1])
.with_cpt_entry("neutral", vec![0.3, 0.5, 0.2])
.with_cpt_entry("bad", vec![0.1, 0.3, 0.6]),
)
.with_node(
"default_probability",
BayesianNode::discrete(vec!["low", "medium", "high"])
.with_parents(vec!["company_revenue"])
.with_cpt_entry("high", vec![0.8, 0.15, 0.05])
.with_cpt_entry("medium", vec![0.4, 0.4, 0.2])
.with_cpt_entry("low", vec![0.1, 0.3, 0.6]),
)
}
#[test]
fn test_adr_credit_risk_example() {
let config = create_credit_risk_network();
let engine = BayesianEngine::new(config).unwrap();
let econ = engine.query("economic_conditions").unwrap();
assert!((econ.probabilities[0] - 0.3).abs() < 0.01); assert!((econ.probabilities[1] - 0.5).abs() < 0.01); assert!((econ.probabilities[2] - 0.2).abs() < 0.01);
let mut evidence = HashMap::new();
evidence.insert("economic_conditions".to_string(), "bad");
let default = engine
.query_with_evidence("default_probability", &evidence)
.unwrap();
let p_high_default = default.probabilities[2]; assert!(
p_high_default > 0.3,
"P(default=high | economy=bad) should be high: {p_high_default}"
);
println!("P(default=high | economy=bad) = {p_high_default:.3}");
}
#[test]
fn test_chain_network() {
let config = BayesianConfig::new("Chain")
.with_node(
"A",
BayesianNode::discrete(vec!["a0", "a1"]).with_prior(vec![0.6, 0.4]),
)
.with_node(
"B",
BayesianNode::discrete(vec!["b0", "b1"])
.with_parents(vec!["A"])
.with_cpt_entry("a0", vec![0.9, 0.1])
.with_cpt_entry("a1", vec![0.2, 0.8]),
)
.with_node(
"C",
BayesianNode::discrete(vec!["c0", "c1"])
.with_parents(vec!["B"])
.with_cpt_entry("b0", vec![0.7, 0.3])
.with_cpt_entry("b1", vec![0.1, 0.9]),
);
let engine = BayesianEngine::new(config).unwrap();
let b_result = engine.query("B").unwrap();
assert!(
(b_result.probabilities[1] - 0.38).abs() < 0.01,
"P(B=b1) should be 0.38, got {}",
b_result.probabilities[1]
);
}
#[test]
#[allow(clippy::similar_names)]
fn test_conditional_independence() {
let config = BayesianConfig::new("CI Test")
.with_node(
"A",
BayesianNode::discrete(vec!["a0", "a1"]).with_prior(vec![0.5, 0.5]),
)
.with_node(
"B",
BayesianNode::discrete(vec!["b0", "b1"])
.with_parents(vec!["A"])
.with_cpt_entry("a0", vec![0.8, 0.2])
.with_cpt_entry("a1", vec![0.2, 0.8]),
)
.with_node(
"C",
BayesianNode::discrete(vec!["c0", "c1"])
.with_parents(vec!["B"])
.with_cpt_entry("b0", vec![0.9, 0.1])
.with_cpt_entry("b1", vec![0.1, 0.9]),
);
let engine = BayesianEngine::new(config).unwrap();
let mut evidence_b = HashMap::new();
evidence_b.insert("B".to_string(), "b1");
let c_given_b = engine.query_with_evidence("C", &evidence_b).unwrap();
let mut evidence_ab = HashMap::new();
evidence_ab.insert("A".to_string(), "a0");
evidence_ab.insert("B".to_string(), "b1");
let c_given_ab = engine.query_with_evidence("C", &evidence_ab).unwrap();
assert!(
(c_given_b.probabilities[0] - c_given_ab.probabilities[0]).abs() < 0.05,
"Conditional independence should hold: P(C|B)={:.3} vs P(C|A,B)={:.3}",
c_given_b.probabilities[0],
c_given_ab.probabilities[0]
);
}
#[test]
fn test_pgmpy_equivalence() {
let config = BayesianConfig::new("Student Network")
.with_node(
"difficulty",
BayesianNode::discrete(vec!["easy", "hard"]).with_prior(vec![0.6, 0.4]),
)
.with_node(
"intelligence",
BayesianNode::discrete(vec!["low", "high"])
.with_parents(vec!["difficulty"])
.with_cpt_entry("easy", vec![0.7, 0.3])
.with_cpt_entry("hard", vec![0.3, 0.7]),
)
.with_node(
"grade",
BayesianNode::discrete(vec!["A", "B", "C"])
.with_parents(vec!["intelligence"])
.with_cpt_entry("low", vec![0.3, 0.4, 0.3])
.with_cpt_entry("high", vec![0.05, 0.25, 0.7]),
);
let engine = BayesianEngine::new(config).unwrap();
let grade = engine.query("grade").unwrap();
let p_a = grade.probabilities[0];
assert!(
(p_a - 0.185).abs() < 0.02,
"P(Grade=A) should be ~0.185, got {p_a}"
);
println!("P(Grade) = {:?}", grade.probabilities);
}
#[test]
fn test_probability_normalization() {
let config = create_credit_risk_network();
let engine = BayesianEngine::new(config).unwrap();
let result = engine.query_all().unwrap();
for (name, var_result) in &result.queries {
let sum: f64 = var_result.probabilities.iter().sum();
assert!(
(sum - 1.0).abs() < 0.001,
"Variable {name} probabilities should sum to 1.0, got {sum}"
);
}
}
#[test]
fn test_evidence_effect() {
let config = create_credit_risk_network();
let engine = BayesianEngine::new(config).unwrap();
let prior = engine.query("default_probability").unwrap();
let p_high_prior = prior.probabilities[2];
let mut evidence = HashMap::new();
evidence.insert("economic_conditions".to_string(), "good");
let posterior = engine
.query_with_evidence("default_probability", &evidence)
.unwrap();
let p_high_posterior = posterior.probabilities[2];
assert!(
p_high_posterior < p_high_prior,
"Good economy should reduce default risk: prior={p_high_prior}, posterior={p_high_posterior}"
);
}
#[test]
fn test_json_with_evidence() {
let config = create_credit_risk_network();
let engine = BayesianEngine::new(config).unwrap();
let mut evidence = HashMap::new();
evidence.insert("economic_conditions".to_string(), "bad");
let result = engine.query_all_with_evidence(&evidence).unwrap();
let json = result.to_json().unwrap();
assert!(json.contains("\"evidence\""));
assert!(json.contains("\"bad\""));
}