iints-sdk-python35 0.0.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. iints/__init__.py +183 -0
  2. iints/analysis/__init__.py +12 -0
  3. iints/analysis/algorithm_xray.py +387 -0
  4. iints/analysis/baseline.py +92 -0
  5. iints/analysis/clinical_benchmark.py +198 -0
  6. iints/analysis/clinical_metrics.py +551 -0
  7. iints/analysis/clinical_tir_analyzer.py +136 -0
  8. iints/analysis/diabetes_metrics.py +43 -0
  9. iints/analysis/edge_efficiency.py +33 -0
  10. iints/analysis/edge_performance_monitor.py +315 -0
  11. iints/analysis/explainability.py +94 -0
  12. iints/analysis/explainable_ai.py +232 -0
  13. iints/analysis/hardware_benchmark.py +221 -0
  14. iints/analysis/metrics.py +117 -0
  15. iints/analysis/population_report.py +188 -0
  16. iints/analysis/reporting.py +345 -0
  17. iints/analysis/safety_index.py +311 -0
  18. iints/analysis/sensor_filtering.py +54 -0
  19. iints/analysis/validator.py +273 -0
  20. iints/api/__init__.py +0 -0
  21. iints/api/base_algorithm.py +307 -0
  22. iints/api/registry.py +103 -0
  23. iints/api/template_algorithm.py +195 -0
  24. iints/assets/iints_logo.png +0 -0
  25. iints/cli/__init__.py +0 -0
  26. iints/cli/cli.py +2598 -0
  27. iints/core/__init__.py +1 -0
  28. iints/core/algorithms/__init__.py +0 -0
  29. iints/core/algorithms/battle_runner.py +138 -0
  30. iints/core/algorithms/correction_bolus.py +95 -0
  31. iints/core/algorithms/discovery.py +92 -0
  32. iints/core/algorithms/fixed_basal_bolus.py +58 -0
  33. iints/core/algorithms/hybrid_algorithm.py +92 -0
  34. iints/core/algorithms/lstm_algorithm.py +138 -0
  35. iints/core/algorithms/mock_algorithms.py +162 -0
  36. iints/core/algorithms/pid_controller.py +88 -0
  37. iints/core/algorithms/standard_pump_algo.py +64 -0
  38. iints/core/device.py +0 -0
  39. iints/core/device_manager.py +64 -0
  40. iints/core/devices/__init__.py +3 -0
  41. iints/core/devices/models.py +160 -0
  42. iints/core/patient/__init__.py +9 -0
  43. iints/core/patient/bergman_model.py +341 -0
  44. iints/core/patient/models.py +285 -0
  45. iints/core/patient/patient_factory.py +117 -0
  46. iints/core/patient/profile.py +41 -0
  47. iints/core/safety/__init__.py +12 -0
  48. iints/core/safety/config.py +37 -0
  49. iints/core/safety/input_validator.py +95 -0
  50. iints/core/safety/supervisor.py +39 -0
  51. iints/core/simulation/__init__.py +0 -0
  52. iints/core/simulation/scenario_parser.py +61 -0
  53. iints/core/simulator.py +874 -0
  54. iints/core/supervisor.py +367 -0
  55. iints/data/__init__.py +53 -0
  56. iints/data/adapter.py +142 -0
  57. iints/data/column_mapper.py +398 -0
  58. iints/data/datasets.json +132 -0
  59. iints/data/demo/__init__.py +1 -0
  60. iints/data/demo/demo_cgm.csv +289 -0
  61. iints/data/importer.py +275 -0
  62. iints/data/ingestor.py +162 -0
  63. iints/data/nightscout.py +128 -0
  64. iints/data/quality_checker.py +550 -0
  65. iints/data/registry.py +166 -0
  66. iints/data/tidepool.py +38 -0
  67. iints/data/universal_parser.py +813 -0
  68. iints/data/virtual_patients/clinic_safe_baseline.yaml +9 -0
  69. iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +9 -0
  70. iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +9 -0
  71. iints/data/virtual_patients/clinic_safe_midnight.yaml +9 -0
  72. iints/data/virtual_patients/clinic_safe_pizza.yaml +9 -0
  73. iints/data/virtual_patients/clinic_safe_stress_meal.yaml +9 -0
  74. iints/data/virtual_patients/default_patient.yaml +11 -0
  75. iints/data/virtual_patients/patient_559_config.yaml +11 -0
  76. iints/emulation/__init__.py +80 -0
  77. iints/emulation/legacy_base.py +414 -0
  78. iints/emulation/medtronic_780g.py +337 -0
  79. iints/emulation/omnipod_5.py +367 -0
  80. iints/emulation/tandem_controliq.py +393 -0
  81. iints/highlevel.py +451 -0
  82. iints/learning/__init__.py +3 -0
  83. iints/learning/autonomous_optimizer.py +194 -0
  84. iints/learning/learning_system.py +122 -0
  85. iints/metrics.py +34 -0
  86. iints/population/__init__.py +11 -0
  87. iints/population/generator.py +131 -0
  88. iints/population/runner.py +327 -0
  89. iints/presets/__init__.py +28 -0
  90. iints/presets/presets.json +114 -0
  91. iints/research/__init__.py +30 -0
  92. iints/research/config.py +68 -0
  93. iints/research/dataset.py +319 -0
  94. iints/research/losses.py +73 -0
  95. iints/research/predictor.py +329 -0
  96. iints/scenarios/__init__.py +3 -0
  97. iints/scenarios/generator.py +92 -0
  98. iints/templates/__init__.py +0 -0
  99. iints/templates/default_algorithm.py +91 -0
  100. iints/templates/scenarios/__init__.py +0 -0
  101. iints/templates/scenarios/chaos_insulin_stacking.json +29 -0
  102. iints/templates/scenarios/chaos_runaway_ai.json +25 -0
  103. iints/templates/scenarios/example_scenario.json +35 -0
  104. iints/templates/scenarios/exercise_stress.json +30 -0
  105. iints/utils/__init__.py +3 -0
  106. iints/utils/plotting.py +50 -0
  107. iints/utils/run_io.py +152 -0
  108. iints/validation/__init__.py +133 -0
  109. iints/validation/schemas.py +94 -0
  110. iints/visualization/__init__.py +34 -0
  111. iints/visualization/cockpit.py +691 -0
  112. iints/visualization/uncertainty_cloud.py +612 -0
  113. iints_sdk_python35-0.0.18.dist-info/METADATA +225 -0
  114. iints_sdk_python35-0.0.18.dist-info/RECORD +118 -0
  115. iints_sdk_python35-0.0.18.dist-info/WHEEL +5 -0
  116. iints_sdk_python35-0.0.18.dist-info/entry_points.txt +10 -0
  117. iints_sdk_python35-0.0.18.dist-info/licenses/LICENSE +28 -0
  118. iints_sdk_python35-0.0.18.dist-info/top_level.txt +1 -0
@@ -0,0 +1,117 @@
1
+ import numpy as np
2
+ from typing import Dict, Any, Optional
3
+ from .models import CustomPatientModel
4
+
5
+ try:
6
+ from simglucose.simulation.env import T1DSimEnv
7
+ from simglucose.patient.t1dpatient import T1DPatient
8
+ from simglucose.sensor.cgm import CGMSensor
9
+ from simglucose.actuator.pump import InsulinPump
10
+ from simglucose.controller.base import Action
11
+ SIMGLUCOSE_AVAILABLE = True
12
+ except ImportError:
13
+ SIMGLUCOSE_AVAILABLE = False
14
+
15
+ class PatientFactory:
16
+ """Factory for creating different types of patient models."""
17
+
18
+ SIMGLUCOSE_PATIENTS = [
19
+ 'adolescent#001', 'adolescent#002', 'adolescent#003', 'adolescent#004', 'adolescent#005',
20
+ 'adolescent#006', 'adolescent#007', 'adolescent#008', 'adolescent#009', 'adolescent#010',
21
+ 'adult#001', 'adult#002', 'adult#003', 'adult#004', 'adult#005',
22
+ 'adult#006', 'adult#007', 'adult#008', 'adult#009', 'adult#010',
23
+ 'child#001', 'child#002', 'child#003', 'child#004', 'child#005',
24
+ 'child#006', 'child#007', 'child#008', 'child#009', 'child#010'
25
+ ]
26
+
27
+ @staticmethod
28
+ def create_patient(patient_type='custom', patient_id=None, initial_glucose=120.0, **kwargs):
29
+ """Create a patient model based on type."""
30
+ if patient_type == 'custom':
31
+ return CustomPatientModel(initial_glucose=initial_glucose, **kwargs)
32
+ elif patient_type == 'simglucose':
33
+ if not SIMGLUCOSE_AVAILABLE:
34
+ print("Warning: Simglucose not available, falling back to custom model")
35
+ return CustomPatientModel(initial_glucose=initial_glucose, **kwargs)
36
+
37
+ patient_name = patient_id or PatientFactory.SIMGLUCOSE_PATIENTS[0]
38
+ return SimglucosePatientWrapper(patient_name, initial_glucose)
39
+ else:
40
+ raise ValueError(f"Unknown patient type: {patient_type}")
41
+
42
+ @staticmethod
43
+ def get_patient_diversity_set():
44
+ """Get a diverse set of patients for population studies."""
45
+ if not SIMGLUCOSE_AVAILABLE:
46
+ # Create diverse custom patients with different parameters
47
+ return [
48
+ CustomPatientModel(initial_glucose=120, insulin_sensitivity=40), # High sensitivity
49
+ CustomPatientModel(initial_glucose=120, insulin_sensitivity=60), # Low sensitivity
50
+ CustomPatientModel(initial_glucose=120, carb_factor=8), # Fast carb absorption
51
+ CustomPatientModel(initial_glucose=120, carb_factor=12), # Slow carb absorption
52
+ CustomPatientModel(initial_glucose=120, glucose_decay_rate=0.0015), # Slow metabolism
53
+ CustomPatientModel(initial_glucose=120, glucose_decay_rate=0.0035), # Fast metabolism
54
+ ]
55
+ else:
56
+ # Use FDA-approved virtual patients
57
+ selected_patients = [
58
+ 'adolescent#001', 'adolescent#005', 'adult#001',
59
+ 'adult#005', 'child#001', 'child#005'
60
+ ]
61
+ return [SimglucosePatientWrapper(name) for name in selected_patients]
62
+
63
+ class SimglucosePatientWrapper:
64
+ """Wrapper for simglucose patients to match CustomPatientModel interface."""
65
+
66
+ def __init__(self, patient_name='adolescent#001', initial_glucose=120.0):
67
+ if not SIMGLUCOSE_AVAILABLE:
68
+ raise ImportError("Simglucose not available")
69
+
70
+ self.patient = T1DPatient.make(patient_name)
71
+ self.sensor = CGMSensor.make()
72
+ self.pump = InsulinPump.make()
73
+ self.env = T1DSimEnv(patient=self.patient, sensor=self.sensor, pump=self.pump)
74
+ self.patient_name = patient_name
75
+ self.reset()
76
+
77
+ def reset(self):
78
+ """Reset the simglucose environment."""
79
+ self.env.reset()
80
+
81
+ def get_current_glucose(self):
82
+ """Get current glucose in mg/dL."""
83
+ return self.env.patient.BG * 18.0156
84
+
85
+ def update(self, time_step, delivered_insulin, carb_intake=0.0, **kwargs):
86
+ """Update patient state."""
87
+ action = Action(basal=0.0, bolus=delivered_insulin, carb=carb_intake)
88
+ obs, reward, done, info = self.env.step(action)
89
+ self._last_info = info
90
+ return obs[0] * 18.0156
91
+
92
+ @property
93
+ def insulin_on_board(self):
94
+ """Get insulin on board."""
95
+ if hasattr(self, '_last_info') and 'IOB' in self._last_info:
96
+ return self._last_info['IOB']
97
+ return getattr(self.env.patient, 'IOB', 0.0)
98
+
99
+ @property
100
+ def carbs_on_board(self):
101
+ """Get carbs on board."""
102
+ if hasattr(self, '_last_info') and 'COB' in self._last_info:
103
+ return self._last_info['COB']
104
+ return getattr(self.env.patient, 'COB', 0.0)
105
+
106
+ def trigger_event(self, event_type, value):
107
+ """Trigger stress events."""
108
+ print(f"SimglucosePatient {self.patient_name}: {event_type} = {value}")
109
+
110
+ def get_patient_state(self):
111
+ """Get patient state for logging."""
112
+ return {
113
+ "current_glucose": self.get_current_glucose(),
114
+ "insulin_on_board": self.insulin_on_board,
115
+ "carbs_on_board": self.carbs_on_board,
116
+ "patient_name": self.patient_name
117
+ }
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Any, Optional
5
+
6
+
7
+ @dataclass
8
+ class PatientProfile:
9
+ """
10
+ User-facing patient profile that maps to the simulator config.
11
+ """
12
+ isf: float = 50.0 # Insulin Sensitivity Factor (mg/dL per unit)
13
+ icr: float = 10.0 # Insulin-to-carb ratio (grams per unit)
14
+ basal_rate: float = 0.8 # U/hr
15
+ initial_glucose: float = 120.0
16
+ dawn_phenomenon_strength: float = 0.0 # mg/dL per hour
17
+ dawn_start_hour: float = 4.0
18
+ dawn_end_hour: float = 8.0
19
+
20
+ # Advanced knobs (optional)
21
+ glucose_decay_rate: float = 0.002
22
+ glucose_absorption_rate: float = 0.03
23
+ insulin_action_duration: float = 300.0
24
+ insulin_peak_time: float = 75.0
25
+ meal_mismatch_epsilon: float = 1.0
26
+
27
+ def to_patient_config(self) -> Dict[str, Any]:
28
+ return {
29
+ "basal_insulin_rate": self.basal_rate,
30
+ "insulin_sensitivity": self.isf,
31
+ "carb_factor": self.icr,
32
+ "initial_glucose": self.initial_glucose,
33
+ "dawn_phenomenon_strength": self.dawn_phenomenon_strength,
34
+ "dawn_start_hour": self.dawn_start_hour,
35
+ "dawn_end_hour": self.dawn_end_hour,
36
+ "glucose_decay_rate": self.glucose_decay_rate,
37
+ "glucose_absorption_rate": self.glucose_absorption_rate,
38
+ "insulin_action_duration": self.insulin_action_duration,
39
+ "insulin_peak_time": self.insulin_peak_time,
40
+ "meal_mismatch_epsilon": self.meal_mismatch_epsilon,
41
+ }
@@ -0,0 +1,12 @@
1
+ from .config import SafetyConfig
2
+ from .input_validator import InputValidator
3
+
4
+ __all__ = ["SafetyConfig", "SafetySupervisor", "InputValidator"]
5
+
6
+
7
+ def __getattr__(name: str):
8
+ if name == "SafetySupervisor":
9
+ from .supervisor import SafetySupervisor
10
+
11
+ return SafetySupervisor
12
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class SafetyConfig:
8
+ """
9
+ Central safety configuration for simulator, input validation, and supervisor.
10
+ """
11
+ # Input validation limits
12
+ min_glucose: float = 20.0
13
+ max_glucose: float = 600.0
14
+ max_glucose_delta_per_5_min: float = 35.0
15
+
16
+ # Supervisor thresholds
17
+ hypoglycemia_threshold: float = 70.0
18
+ severe_hypoglycemia_threshold: float = 54.0
19
+ hyperglycemia_threshold: float = 250.0
20
+ max_insulin_per_bolus: float = 5.0
21
+ glucose_rate_alarm: float = 5.0
22
+ max_insulin_per_hour: float = 3.0
23
+ max_iob: float = 4.0
24
+ trend_stop: float = -2.0
25
+ hypo_cutoff: float = 70.0
26
+ max_basal_multiplier: float = 3.0
27
+ predicted_hypoglycemia_threshold: float = 60.0
28
+ predicted_hypoglycemia_horizon_minutes: int = 30
29
+
30
+ # Formal safety contract (logic validation)
31
+ contract_enabled: bool = True
32
+ contract_glucose_threshold: float = 90.0
33
+ contract_trend_threshold_mgdl_min: float = -1.0 # -5 mg/dL per 5 minutes
34
+
35
+ # Simulation termination limits
36
+ critical_glucose_threshold: float = 40.0
37
+ critical_glucose_duration_minutes: int = 30
@@ -0,0 +1,95 @@
1
+ from typing import Optional
2
+
3
+ from iints.core.safety.config import SafetyConfig
4
+
5
+ class InputValidator:
6
+ """
7
+ A biological validation filter for sensor inputs to ensure they are
8
+ physiologically plausible before being used by an algorithm.
9
+ This component makes the system robust against common sensor errors.
10
+ """
11
+ def __init__(self,
12
+ min_glucose: float = 20.0,
13
+ max_glucose: float = 600.0,
14
+ max_glucose_delta_per_5_min: float = 35.0,
15
+ safety_config: Optional[SafetyConfig] = None):
16
+ """
17
+ Initializes the validator with plausible biological limits.
18
+
19
+ Args:
20
+ min_glucose (float): The absolute minimum plausible glucose value (mg/dL).
21
+ max_glucose (float): The absolute maximum plausible glucose value (mg/dL).
22
+ max_glucose_delta_per_5_min (float): The maximum plausible change in glucose
23
+ over a 5-minute period (mg/dL).
24
+ """
25
+ if safety_config is not None:
26
+ min_glucose = safety_config.min_glucose
27
+ max_glucose = safety_config.max_glucose
28
+ max_glucose_delta_per_5_min = safety_config.max_glucose_delta_per_5_min
29
+
30
+ self.min_glucose = min_glucose
31
+ self.max_glucose = max_glucose
32
+ self.max_glucose_delta_per_5_min = max_glucose_delta_per_5_min
33
+ self.last_valid_glucose: Optional[float] = None
34
+ self.last_validation_time: Optional[float] = None
35
+
36
+ def reset(self):
37
+ """Resets the state of the validator for a new simulation."""
38
+ self.last_valid_glucose = None
39
+ self.last_validation_time = None
40
+
41
+ def get_state(self) -> dict:
42
+ return {
43
+ "last_valid_glucose": self.last_valid_glucose,
44
+ "last_validation_time": self.last_validation_time,
45
+ }
46
+
47
+ def set_state(self, state: dict) -> None:
48
+ self.last_valid_glucose = state.get("last_valid_glucose")
49
+ self.last_validation_time = state.get("last_validation_time")
50
+
51
+ def validate_glucose(self, glucose_value: float, current_time: float) -> float:
52
+ """
53
+ Validates a glucose reading against absolute and rate-of-change limits.
54
+
55
+ Args:
56
+ glucose_value (float): The incoming glucose reading from the sensor.
57
+ current_time (float): The current simulation time in minutes.
58
+
59
+ Returns:
60
+ float: The validated glucose value.
61
+
62
+ Raises:
63
+ ValueError: If the value is outside biological plausibility limits.
64
+ """
65
+ # 1. Absolute biological plausibility check
66
+ if not (self.min_glucose <= glucose_value <= self.max_glucose):
67
+ raise ValueError(
68
+ f"BIOLOGICAL_PLAUSIBILITY_ERROR: Glucose {glucose_value} mg/dL is outside the "
69
+ f"valid range [{self.min_glucose}, {self.max_glucose}]."
70
+ )
71
+
72
+ # 2. Rate-of-change check for unrealistic jumps
73
+ if self.last_valid_glucose is not None and self.last_validation_time is not None:
74
+ time_delta = current_time - self.last_validation_time
75
+ if time_delta > 0:
76
+ # Normalize the max allowed delta to the actual time step
77
+ allowed_delta = self.max_glucose_delta_per_5_min * (time_delta / 5.0)
78
+ glucose_delta = abs(glucose_value - self.last_valid_glucose)
79
+
80
+ if glucose_delta > allowed_delta:
81
+ raise ValueError(
82
+ f"RATE_OF_CHANGE_ERROR: Glucose jump of {glucose_delta:.1f} mg/dL over "
83
+ f"{time_delta:.1f} min is unrealistic (max allowed: {allowed_delta:.1f} mg/dL)."
84
+ )
85
+
86
+ # If all checks pass, update state and return the value
87
+ self.last_valid_glucose = glucose_value
88
+ self.last_validation_time = current_time
89
+ return glucose_value
90
+
91
+ def validate_insulin(self, dose: float) -> float:
92
+ """Validates that a proposed insulin dose is non-negative."""
93
+ if dose < 0:
94
+ return 0.0
95
+ return dose
@@ -0,0 +1,39 @@
1
+ from typing import Optional
2
+
3
+ from iints.core.safety.config import SafetyConfig
4
+ from iints.core.supervisor import IndependentSupervisor as FullSupervisor
5
+
6
+ class IndependentSupervisor(FullSupervisor):
7
+ """
8
+ Safety supervisor that operates independently to validate insulin delivery.
9
+ Enforces hard constraints on insulin delivery to prevent hypoglycemia and other hazards.
10
+ """
11
+ def __init__(self, safety_config: Optional[SafetyConfig] = None):
12
+ super().__init__(safety_config=safety_config)
13
+
14
+ def validate_insulin_dose(
15
+ self,
16
+ proposed_dose: float,
17
+ current_glucose: float,
18
+ active_insulin: float,
19
+ time_since_last_dose: float,
20
+ ) -> float:
21
+ """
22
+ Backward-compatible API that routes through the full supervisor.
23
+ """
24
+ result = self.evaluate_safety(
25
+ current_glucose=current_glucose,
26
+ proposed_insulin=proposed_dose,
27
+ current_time=0.0,
28
+ current_iob=active_insulin,
29
+ )
30
+ return result["approved_insulin"]
31
+
32
+ # Alias for backward compatibility as the codebase migrates
33
+ SafetySupervisor = IndependentSupervisor
34
+
35
+ class InputValidator:
36
+ """
37
+ Validates simulation inputs.
38
+ """
39
+ pass
File without changes
@@ -0,0 +1,61 @@
1
+ # src/simulation/scenario_parser.py
2
+
3
+ from typing import List, Dict, Any, Tuple
4
+
5
+ from iints.core.simulator import StressEvent
6
+ from iints.validation import load_scenario, scenario_to_payloads, build_stress_events
7
+
8
+ def parse_scenario(file_path: str) -> Tuple[Dict[str, Any], List[StressEvent]]:
9
+ """
10
+ Parses a scenario from a JSON file.
11
+
12
+ Args:
13
+ file_path: The path to the scenario JSON file.
14
+
15
+ Returns:
16
+ A tuple containing:
17
+ - A dictionary with scenario metadata (name, description).
18
+ - A list of StressEvent objects.
19
+
20
+ Raises:
21
+ ValueError: If the file format or content is invalid.
22
+ """
23
+ try:
24
+ scenario = load_scenario(file_path)
25
+ except Exception as e:
26
+ raise ValueError(f"Invalid scenario file {file_path}: {e}")
27
+
28
+ metadata = {
29
+ "name": scenario.scenario_name,
30
+ "description": scenario.description or "",
31
+ "version": scenario.scenario_version,
32
+ "source_file": file_path,
33
+ }
34
+
35
+ payloads = scenario_to_payloads(scenario)
36
+ events: List[StressEvent] = build_stress_events(payloads)
37
+ return metadata, events
38
+
39
+ if __name__ == '__main__':
40
+ # A simple test to demonstrate the scenario parser
41
+ print("--- Testing Scenario Parser ---")
42
+
43
+ # Use the example scenario created earlier
44
+ example_path = "scenarios/example_scenario.json"
45
+
46
+ try:
47
+ print(f"Parsing scenario file: {example_path}")
48
+ scenario_metadata, scenario_events = parse_scenario(example_path)
49
+
50
+ print("\nSuccessfully parsed scenario:")
51
+ print(f" Name: {scenario_metadata['name']}")
52
+ print(f" Description: {scenario_metadata['description']}")
53
+
54
+ print("\nEvents:")
55
+ for ev in scenario_events:
56
+ print(f" - {ev}")
57
+
58
+ except ValueError as e:
59
+ print(f"\nError parsing scenario: {e}")
60
+ except Exception as e:
61
+ print(f"\nAn unexpected error occurred: {e}")