1use xlog_core::{Result, XlogError};
4
5use super::{McProgram, McSamplingMethod};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum ForceabilityReason {
10 AllForceable,
12 ContainsDerivedEvidence,
14 ContainsNegativeAdHeadEvidence,
16 NoEvidence,
18}
19
20#[derive(Debug, Clone)]
22pub struct EvidenceForcing {
23 pub force_mask: Vec<u8>,
25 pub forced_value: Vec<u8>,
27 pub forceable: bool,
29 pub reason: ForceabilityReason,
31}
32
33impl McProgram {
34 pub(super) fn resolve_sampling_method(
36 &self,
37 requested: Option<McSamplingMethod>,
38 ) -> Result<(McSamplingMethod, EvidenceForcing)> {
39 let forcing = self.compile_evidence_forcing()?;
40 let method = match requested {
41 Some(McSamplingMethod::EvidenceClamping) => {
42 if !forcing.forceable {
43 return Err(XlogError::Execution(format!(
44 "Cannot use EvidenceClamping: {:?}",
45 forcing.reason
46 )));
47 }
48 McSamplingMethod::EvidenceClamping
49 }
50 Some(McSamplingMethod::Rejection) => McSamplingMethod::Rejection,
51 None => {
52 if forcing.forceable {
53 McSamplingMethod::EvidenceClamping
54 } else {
55 McSamplingMethod::Rejection
56 }
57 }
58 };
59 Ok((method, forcing))
60 }
61
62 pub fn compile_evidence_forcing(&self) -> Result<EvidenceForcing> {
64 let num_vars = self.bernoulli_probs.len();
65 let mut force_mask = vec![0u8; num_vars];
66 let mut forced_value = vec![0u8; num_vars];
67
68 if self.evidence.is_empty() {
69 return Ok(EvidenceForcing {
70 force_mask,
71 forced_value,
72 forceable: false,
73 reason: ForceabilityReason::NoEvidence,
74 });
75 }
76
77 for (atom, expected) in &self.evidence {
78 if let Some(spec) = self.prob_facts.iter().find(|s| &s.atom == atom) {
80 force_mask[spec.var_idx] = 1;
81 forced_value[spec.var_idx] = if *expected { 1 } else { 0 };
82 continue;
83 }
84
85 let mut found_ad = false;
87 for ad in &self.annotated_disjunctions {
88 if let Some(choice_idx) = ad.choices.iter().position(|c| c == atom) {
89 if !*expected {
90 return Ok(EvidenceForcing {
92 force_mask: vec![0u8; num_vars],
93 forced_value: vec![0u8; num_vars],
94 forceable: false,
95 reason: ForceabilityReason::ContainsNegativeAdHeadEvidence,
96 });
97 }
98
99 let num_decision_vars = ad.decision_vars.len();
100 if choice_idx < num_decision_vars {
101 for i in 0..choice_idx {
103 force_mask[ad.decision_vars[i]] = 1;
104 forced_value[ad.decision_vars[i]] = 0;
105 }
106 force_mask[ad.decision_vars[choice_idx]] = 1;
107 forced_value[ad.decision_vars[choice_idx]] = 1;
108 } else {
109 for &dv in &ad.decision_vars {
111 force_mask[dv] = 1;
112 forced_value[dv] = 0;
113 }
114 }
115 found_ad = true;
116 break;
117 }
118 }
119 if found_ad {
120 continue;
121 }
122
123 return Ok(EvidenceForcing {
125 force_mask: vec![0u8; num_vars],
126 forced_value: vec![0u8; num_vars],
127 forceable: false,
128 reason: ForceabilityReason::ContainsDerivedEvidence,
129 });
130 }
131
132 Ok(EvidenceForcing {
133 force_mask,
134 forced_value,
135 forceable: true,
136 reason: ForceabilityReason::AllForceable,
137 })
138 }
139}