Skip to main content

qualia_core_db/specialized_libs/medical_computing/
diagnosis.rs

1use super::*;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5/// Clinical analyzer for medical data analysis
6pub struct ClinicalAnalyzer {
7    diagnostic_engine: DiagnosticEngine,
8    risk_assessment: ClinicalRiskAssessment,
9    treatment_planner: TreatmentPlanner,
10    outcome_predictor: OutcomePredictor,
11}
12
13/// Diagnostic engine
14pub struct DiagnosticEngine {
15    diagnostic_algorithms: HashMap<String, DiagnosticAlgorithm>,
16    symptom_analyzer: SymptomAnalyzer,
17    lab_interpreter: LabInterpreter,
18}
19
20/// Diagnostic algorithms
21#[derive(Debug, Clone)]
22pub struct DiagnosticAlgorithm {
23    pub algorithm_id: String,
24    pub algorithm_name: String,
25    pub algorithm_type: DiagnosticAlgorithmType,
26    pub accuracy: f64,
27}
28
29/// Diagnostic algorithm types
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub enum DiagnosticAlgorithmType {
32    RuleBased,
33    MachineLearning,
34    Bayesian,
35    NeuralNetwork,
36}
37
38/// Symptom analyzer
39pub struct SymptomAnalyzer {
40    symptom_patterns: HashMap<String, SymptomPattern>,
41    symptom_correlations: HashMap<String, SymptomCorrelation>,
42}
43
44/// Symptom patterns
45#[derive(Debug, Clone)]
46pub struct SymptomPattern {
47    pub pattern_id: String,
48    pub pattern_name: String,
49    pub symptoms: Vec<String>,
50    pub associated_conditions: Vec<String>,
51}
52
53/// Symptom correlations
54#[derive(Debug, Clone)]
55pub struct SymptomCorrelation {
56    pub correlation_id: String,
57    pub symptom1: String,
58    pub symptom2: String,
59    pub correlation_coefficient: f64,
60}
61
62/// Lab interpreter
63pub struct LabInterpreter {
64    reference_ranges: HashMap<String, ReferenceRange>,
65    abnormality_detector: AbnormalityDetector,
66}
67
68/// Abnormality detector
69#[derive(Debug, Clone)]
70pub struct AbnormalityDetector {
71    detection_algorithms: HashMap<String, DetectionAlgorithm>,
72    severity_assessment: SeverityAssessment,
73}
74
75/// Severity assessment
76#[derive(Debug, Clone)]
77pub struct SeverityAssessment {
78    assessment_criteria: HashMap<String, AssessmentCriterion>,
79    scoring_system: ScoringSystem,
80}
81
82/// Assessment criteria
83#[derive(Debug, Clone)]
84pub struct AssessmentCriterion {
85    pub criterion_id: String,
86    pub criterion_name: String,
87    pub weight: f64,
88    pub threshold: f64,
89}
90
91/// Scoring system
92#[derive(Debug, Clone)]
93pub struct ScoringSystem {
94    pub system_id: String,
95    pub system_name: String,
96    pub scoring_algorithm: ScoringAlgorithm,
97}
98
99/// Scoring algorithms
100#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub enum ScoringAlgorithm {
102    WeightedSum,
103    Bayesian,
104    FuzzyLogic,
105    NeuralNetwork,
106}
107
108/// Clinical risk assessment
109pub struct ClinicalRiskAssessment {
110    risk_models: HashMap<String, ClinicalRiskModel>,
111    risk_factors: HashMap<String, ClinicalRiskFactor>,
112}
113
114/// Clinical risk models
115#[derive(Debug, Clone)]
116pub struct ClinicalRiskModel {
117    pub model_id: String,
118    pub model_name: String,
119    pub model_type: ClinicalRiskModelType,
120    pub validation_results: ValidationResults,
121}
122
123/// Clinical risk model types
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub enum ClinicalRiskModelType {
126    Cardiovascular,
127    Cancer,
128    Diabetes,
129    Respiratory,
130    Custom(String),
131}
132
133/// Validation results
134#[derive(Debug, Clone)]
135pub struct ValidationResults {
136    pub accuracy: f64,
137    pub sensitivity: f64,
138    pub specificity: f64,
139    pub auc: f64,
140}
141
142/// Clinical risk factors
143#[derive(Debug, Clone)]
144pub struct ClinicalRiskFactor {
145    pub factor_id: String,
146    pub factor_name: String,
147    pub factor_category: FactorCategory,
148    pub factor_weight: f64,
149}
150
151/// Factor categories
152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
153pub enum FactorCategory {
154    Demographic,
155    Lifestyle,
156    Medical,
157    Genetic,
158    Environmental,
159}
160
161/// Treatment planner
162pub struct TreatmentPlanner {
163    treatment_guidelines: HashMap<String, TreatmentGuideline>,
164    decision_support: DecisionSupport,
165}
166
167/// Treatment guidelines
168#[derive(Debug, Clone)]
169pub struct TreatmentGuideline {
170    pub guideline_id: String,
171    pub guideline_name: String,
172    pub guideline_type: GuidelineType,
173    pub recommendations: Vec<TreatmentRecommendation>,
174}
175
176/// Guideline types
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
178pub enum GuidelineType {
179    Clinical,
180    Protocol,
181    StandardOfCare,
182    BestPractice,
183}
184
185/// Treatment recommendations
186#[derive(Debug, Clone)]
187pub struct TreatmentRecommendation {
188    pub recommendation_id: String,
189    pub condition: String,
190    pub treatment: String,
191    pub evidence_level: EvidenceLevel,
192    pub strength: RecommendationStrength,
193}
194
195/// Evidence levels
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
197pub enum EvidenceLevel {
198    LevelA,
199    LevelB,
200    LevelC,
201    ExpertOpinion,
202}
203
204/// Recommendation strength
205#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206pub enum RecommendationStrength {
207    Strong,
208    Moderate,
209    Weak,
210    ExpertConsensus,
211}
212
213/// Decision support
214pub struct DecisionSupport {
215    decision_trees: HashMap<String, DecisionTree>,
216    scoring_systems: HashMap<String, ScoringSystem>,
217}
218
219/// Decision trees
220#[derive(Debug, Clone)]
221pub struct DecisionTree {
222    pub tree_id: String,
223    pub tree_name: String,
224    pub root_node: DecisionNode,
225}
226
227/// Decision nodes
228#[derive(Debug, Clone)]
229pub struct DecisionNode {
230    pub node_id: String,
231    pub node_type: NodeType,
232    pub condition: Option<String>,
233    pub threshold: Option<f64>,
234    pub children: Vec<DecisionNode>,
235    pub outcome: Option<String>,
236}
237
238/// Node types
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub enum NodeType {
241    Root,
242    Decision,
243    Leaf,
244}
245
246/// Outcome predictor
247pub struct OutcomePredictor {
248    prediction_models: HashMap<String, PredictionModel>,
249    outcome_metrics: HashMap<String, OutcomeMetric>,
250}
251
252/// Prediction models
253#[derive(Debug, Clone)]
254pub struct PredictionModel {
255    pub model_id: String,
256    pub model_name: String,
257    pub model_type: PredictionModelType,
258    pub performance_metrics: ModelPerformanceMetrics,
259}
260
261/// Prediction model types
262#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
263pub enum PredictionModelType {
264    Survival,
265    Response,
266    Recurrence,
267    Complication,
268}
269
270/// Model performance metrics
271#[derive(Debug, Clone)]
272pub struct ModelPerformanceMetrics {
273    pub accuracy: f64,
274    pub precision: f64,
275    pub recall: f64,
276    pub f1_score: f64,
277}
278
279/// Outcome metrics
280#[derive(Debug, Clone)]
281pub struct OutcomeMetric {
282    pub metric_id: String,
283    pub metric_name: String,
284    pub metric_type: OutcomeMetricType,
285    pub measurement_method: MeasurementMethod,
286}
287
288/// Outcome metric types
289#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
290pub enum OutcomeMetricType {
291    Mortality,
292    Morbidity,
293    QualityOfLife,
294    FunctionalStatus,
295}
296
297/// Measurement methods
298#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
299pub enum MeasurementMethod {
300    Scale,
301    Binary,
302    Continuous,
303    Categorical,
304}
305impl ClinicalAnalyzer {
306    pub fn new() -> Self {
307        Self {
308            diagnostic_engine: DiagnosticEngine::new(),
309            risk_assessment: ClinicalRiskAssessment::new(),
310            treatment_planner: TreatmentPlanner::new(),
311            outcome_predictor: OutcomePredictor::new(),
312        }
313    }
314
315    pub fn initialize(&mut self) -> Result<(), MedicalError> {
316        self.diagnostic_engine.initialize()?;
317        self.risk_assessment.initialize()?;
318        self.treatment_planner.initialize()?;
319        self.outcome_predictor.initialize()?;
320        Ok(())
321    }
322
323    /// Patient-only convenience entry point. The real differential engine
324    /// ([`Self::analyze_differential`]) requires a CALLER-SUPPLIED knowledge base
325    /// (priors + per-finding likelihoods); this overload is handed no such KB, so it
326    /// fails closed with `InsufficientData` rather than fabricating a diagnosis or a
327    /// confidence. Supply findings + a knowledge base via `analyze_differential` to get
328    /// a real, ranked, honestly-labeled proposal.
329    pub fn analyze_data(
330        &mut self,
331        _patient: &Patient,
332        _data_type: ClinicalDataType,
333    ) -> Result<ClinicalAnalysis, MedicalError> {
334        Err(MedicalError::InsufficientData(
335            "clinical diagnostic analysis (analyze_clinical_data): no knowledge base was \
336             supplied through the patient-only path. Use analyze_differential(findings, \
337             knowledge_base) with a caller-supplied, non-authoritative KB to obtain a ranked \
338             epistemic proposal. Refusing to emit a fabricated diagnosis or confidence."
339                .to_string(),
340        ))
341    }
342
343    /// Real transparent Bayesian differential over a **caller-supplied, non-authoritative**
344    /// knowledge base. Delegates to [`super::analyze_differential`]. Returns a ranked
345    /// epistemic proposal (never a diagnosis); the honest label lives in
346    /// `DifferentialProposal::epistemic_status`.
347    pub fn analyze_differential(
348        &self,
349        observed_findings: &[String],
350        kb: &super::DiagnosticKnowledgeBase,
351    ) -> Result<super::DifferentialProposal, MedicalError> {
352        super::analyze_differential(observed_findings, kb)
353    }
354}
355
356impl DiagnosticEngine {
357    pub fn new() -> Self {
358        Self {
359            diagnostic_algorithms: HashMap::new(),
360            symptom_analyzer: SymptomAnalyzer::new(),
361            lab_interpreter: LabInterpreter::new(),
362        }
363    }
364
365    pub fn initialize(&mut self) -> Result<(), MedicalError> {
366        Ok(())
367    }
368
369    pub fn add_diagnostic_algorithm(&mut self, algorithm: DiagnosticAlgorithm) {
370        self.diagnostic_algorithms
371            .insert(algorithm.algorithm_id.clone(), algorithm);
372    }
373
374    pub fn get_diagnostic_algorithm(&self, algorithm_id: &str) -> Option<&DiagnosticAlgorithm> {
375        self.diagnostic_algorithms.get(algorithm_id)
376    }
377
378    pub fn symptom_analyzer(&self) -> &SymptomAnalyzer {
379        &self.symptom_analyzer
380    }
381
382    pub fn lab_interpreter(&self) -> &LabInterpreter {
383        &self.lab_interpreter
384    }
385}
386
387impl SymptomAnalyzer {
388    pub fn new() -> Self {
389        Self {
390            symptom_patterns: HashMap::new(),
391            symptom_correlations: HashMap::new(),
392        }
393    }
394
395    pub fn add_symptom_pattern(&mut self, pattern: SymptomPattern) {
396        self.symptom_patterns
397            .insert(pattern.pattern_id.clone(), pattern);
398    }
399
400    pub fn get_symptom_pattern(&self, pattern_id: &str) -> Option<&SymptomPattern> {
401        self.symptom_patterns.get(pattern_id)
402    }
403
404    pub fn add_symptom_correlation(&mut self, correlation: SymptomCorrelation) {
405        self.symptom_correlations
406            .insert(correlation.correlation_id.clone(), correlation);
407    }
408
409    pub fn get_symptom_correlation(&self, correlation_id: &str) -> Option<&SymptomCorrelation> {
410        self.symptom_correlations.get(correlation_id)
411    }
412}
413
414impl LabInterpreter {
415    pub fn new() -> Self {
416        Self {
417            reference_ranges: HashMap::new(),
418            abnormality_detector: AbnormalityDetector::new(),
419        }
420    }
421
422    pub fn add_reference_range(&mut self, test_code: &str, range: ReferenceRange) {
423        self.reference_ranges.insert(test_code.to_string(), range);
424    }
425
426    pub fn get_reference_range(&self, test_code: &str) -> Option<&ReferenceRange> {
427        self.reference_ranges.get(test_code)
428    }
429
430    pub fn abnormality_detector(&self) -> &AbnormalityDetector {
431        &self.abnormality_detector
432    }
433
434    pub fn interpret_result(&self, result: &LabResult) -> ResultStatus {
435        if let Some(range) = self.reference_ranges.get(&result.test_code) {
436            if result.value < range.minimum || result.value > range.maximum {
437                ResultStatus::Abnormal
438            } else {
439                ResultStatus::Normal
440            }
441        } else {
442            result.status.clone()
443        }
444    }
445}
446
447impl AbnormalityDetector {
448    pub fn new() -> Self {
449        Self {
450            detection_algorithms: HashMap::new(),
451            severity_assessment: SeverityAssessment::new(),
452        }
453    }
454
455    pub fn add_detection_algorithm(&mut self, algorithm: DetectionAlgorithm) {
456        self.detection_algorithms
457            .insert(algorithm.algorithm_id.clone(), algorithm);
458    }
459
460    pub fn get_detection_algorithm(&self, algorithm_id: &str) -> Option<&DetectionAlgorithm> {
461        self.detection_algorithms.get(algorithm_id)
462    }
463
464    pub fn severity_assessment(&self) -> &SeverityAssessment {
465        &self.severity_assessment
466    }
467}
468
469impl SeverityAssessment {
470    pub fn new() -> Self {
471        Self {
472            assessment_criteria: HashMap::new(),
473            scoring_system: ScoringSystem::new(),
474        }
475    }
476
477    pub fn add_criterion(&mut self, criterion: AssessmentCriterion) {
478        self.assessment_criteria
479            .insert(criterion.criterion_id.clone(), criterion);
480    }
481
482    pub fn get_criterion(&self, criterion_id: &str) -> Option<&AssessmentCriterion> {
483        self.assessment_criteria.get(criterion_id)
484    }
485
486    pub fn scoring_system(&self) -> &ScoringSystem {
487        &self.scoring_system
488    }
489}
490
491impl ScoringSystem {
492    pub fn new() -> Self {
493        Self {
494            system_id: "system_1".to_string(),
495            system_name: "Clinical Scoring System".to_string(),
496            scoring_algorithm: ScoringAlgorithm::WeightedSum,
497        }
498    }
499}
500
501impl ClinicalRiskAssessment {
502    pub fn new() -> Self {
503        Self {
504            risk_models: HashMap::new(),
505            risk_factors: HashMap::new(),
506        }
507    }
508
509    pub fn initialize(&mut self) -> Result<(), MedicalError> {
510        Ok(())
511    }
512
513    pub fn add_risk_model(&mut self, model: ClinicalRiskModel) {
514        self.risk_models.insert(model.model_id.clone(), model);
515    }
516
517    pub fn get_risk_model(&self, model_id: &str) -> Option<&ClinicalRiskModel> {
518        self.risk_models.get(model_id)
519    }
520
521    pub fn add_risk_factor(&mut self, factor: ClinicalRiskFactor) {
522        self.risk_factors.insert(factor.factor_id.clone(), factor);
523    }
524
525    pub fn get_risk_factor(&self, factor_id: &str) -> Option<&ClinicalRiskFactor> {
526        self.risk_factors.get(factor_id)
527    }
528
529    pub fn compute_risk_score(&self, factor_ids: &[String]) -> f64 {
530        if factor_ids.is_empty() {
531            return 0.0;
532        }
533        let total_weight: f64 = factor_ids
534            .iter()
535            .filter_map(|id| self.risk_factors.get(id))
536            .map(|f| f.factor_weight)
537            .sum();
538        total_weight / factor_ids.len() as f64
539    }
540}
541
542impl TreatmentPlanner {
543    pub fn new() -> Self {
544        Self {
545            treatment_guidelines: HashMap::new(),
546            decision_support: DecisionSupport::new(),
547        }
548    }
549
550    pub fn initialize(&mut self) -> Result<(), MedicalError> {
551        Ok(())
552    }
553
554    pub fn add_treatment_guideline(&mut self, guideline: TreatmentGuideline) {
555        self.treatment_guidelines
556            .insert(guideline.guideline_id.clone(), guideline);
557    }
558
559    pub fn get_treatment_guideline(&self, guideline_id: &str) -> Option<&TreatmentGuideline> {
560        self.treatment_guidelines.get(guideline_id)
561    }
562
563    pub fn decision_support(&self) -> &DecisionSupport {
564        &self.decision_support
565    }
566}
567
568impl DecisionSupport {
569    pub fn new() -> Self {
570        Self {
571            decision_trees: HashMap::new(),
572            scoring_systems: HashMap::new(),
573        }
574    }
575
576    pub fn add_decision_tree(&mut self, tree: DecisionTree) {
577        self.decision_trees.insert(tree.tree_id.clone(), tree);
578    }
579
580    pub fn get_decision_tree(&self, tree_id: &str) -> Option<&DecisionTree> {
581        self.decision_trees.get(tree_id)
582    }
583
584    pub fn add_scoring_system(&mut self, system: ScoringSystem) {
585        self.scoring_systems
586            .insert(system.system_id.clone(), system);
587    }
588
589    pub fn get_scoring_system(&self, system_id: &str) -> Option<&ScoringSystem> {
590        self.scoring_systems.get(system_id)
591    }
592}
593
594impl OutcomePredictor {
595    pub fn new() -> Self {
596        Self {
597            prediction_models: HashMap::new(),
598            outcome_metrics: HashMap::new(),
599        }
600    }
601
602    pub fn initialize(&mut self) -> Result<(), MedicalError> {
603        Ok(())
604    }
605
606    pub fn add_prediction_model(&mut self, model_id: &str, model: PredictionModel) {
607        self.prediction_models.insert(model_id.to_string(), model);
608    }
609
610    pub fn get_prediction_model(&self, model_id: &str) -> Option<&PredictionModel> {
611        self.prediction_models.get(model_id)
612    }
613
614    pub fn add_outcome_metric(&mut self, metric_id: &str, value: OutcomeMetric) {
615        self.outcome_metrics.insert(metric_id.to_string(), value);
616    }
617
618    pub fn get_outcome_metric(&self, metric_id: &str) -> Option<&OutcomeMetric> {
619        self.outcome_metrics.get(metric_id)
620    }
621}