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,367 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Omnipod 5 Emulator - IINTS-AF
4
+ Emulates the Omnipod 5 with Horizon Algorithm.
5
+
6
+ Based on:
7
+ - Clinical studies ( ASSERT, ONSET)
8
+ - FDA 510(k) clearance documentation
9
+ - Technical specifications
10
+
11
+ Part of the #WeAreNotWaiting movement for transparent diabetes tech.
12
+ """
13
+
14
+ import numpy as np
15
+ from typing import Dict, List, Optional, Any
16
+ from dataclasses import dataclass
17
+
18
+ from .legacy_base import (
19
+ LegacyEmulator, PumpBehavior, PIDParameters, SafetyLimits,
20
+ SafetyLevel, EmulatorDecision
21
+ )
22
+
23
+
24
+ @dataclass
25
+ class Omnipod5Behavior(PumpBehavior):
26
+ """Omnipod 5 specific behavior profile"""
27
+ # Omnipod 5 specific parameters
28
+ adaptive_learning: bool = True
29
+ target_glucose: float = 110.0 # mg/dL (range 100-150)
30
+ max_bolus: float = 30.0 # Omnipod allows larger boluses
31
+ max_basal: float = 3.0 # units/hour (pod-specific)
32
+ delivery_frequency: int = 5 # minutes between deliveries
33
+ learning_window: int = 672 # hours (4 weeks) for adaptation
34
+
35
+ # Safety features
36
+ automatic_suspend: bool = True
37
+ low_glucose_suspend: bool = True
38
+ predictive_suspend: bool = True
39
+
40
+ def __init__(self):
41
+ super().__init__(
42
+ pump_name="Omnipod 5 with Horizon Algorithm",
43
+ manufacturer="Insulet Corporation",
44
+ safety_limits=SafetyLimits(
45
+ low_suspend_threshold=67.0, # Omnipod uses 67 mg/dL
46
+ high_suspend_threshold=250.0,
47
+ max_bolus=30.0,
48
+ max_basal_rate=3.0,
49
+ max_daily_total=80.0,
50
+ auto_off_duration=60.0,
51
+ low_suspend_duration=5.0,
52
+ target_glucose=110.0
53
+ ),
54
+ pid_parameters=PIDParameters(
55
+ kp=0.025, # Adaptive PID tuning
56
+ ki=0.003,
57
+ kd=0.035,
58
+ target_glucose=110.0,
59
+ integral_limit=4.0
60
+ ),
61
+ correction_factor=45.0,
62
+ carb_ratio=8.0, # Omnipod default
63
+ insulin_sensitivity_factor=45.0,
64
+ active_insulin_duration=5.0, # Omnipod uses 5-hour DIA
65
+ safety_level=SafetyLevel.MODERATE
66
+ )
67
+
68
+
69
+ class Omnipod5Emulator(LegacyEmulator):
70
+ """
71
+ Emulates Omnipod 5 with Horizon algorithm.
72
+
73
+ Omnipod 5 is a tubeless, patch pump with:
74
+ - Adaptive learning (adjusts based on user patterns)
75
+ - Automatic insulin delivery every 5 minutes
76
+ - Activity and sleep mode support
77
+ - Customizable target glucose (100-150 mg/dL)
78
+
79
+ Key characteristics:
80
+ - Tubeless design (no tubing)
81
+ - Adaptive algorithm learns user patterns
82
+ - Conservative low glucose threshold (67 mg/dL)
83
+ - Activity/Sleep modes with higher targets
84
+
85
+ Sources:
86
+ - ASSERT Trial Results
87
+ - ONSET Trial Results
88
+ - FDA 510(k) K203467
89
+ - Omnipod 5 User Guide
90
+ """
91
+
92
+ def __init__(self):
93
+ """Initialize the Omnipod 5 emulator"""
94
+ super().__init__()
95
+ self.behavior = Omnipod5Behavior()
96
+ self._learning_model = {
97
+ 'user_sensitivity': 50.0,
98
+ 'correction_factor': 50.0,
99
+ 'carb_ratio': 10.0,
100
+ 'basal_rate': 1.0,
101
+ 'patterns_learned': 0
102
+ }
103
+
104
+ def _get_default_behavior(self) -> Omnipod5Behavior:
105
+ """Get Omnipod 5 default behavior"""
106
+ return Omnipod5Behavior()
107
+
108
+ def get_sources(self) -> List[Dict[str, str]]:
109
+ """Get sources for Omnipod 5 emulation logic"""
110
+ return [
111
+ {
112
+ 'title': 'ASSERT Trial - Omnipod 5 Pivotal Study',
113
+ 'type': 'clinical_study',
114
+ 'year': '2021',
115
+ 'url': 'https://www.omnipod.com/assert-trial'
116
+ },
117
+ {
118
+ 'title': 'ONSET Trial - Omnipod 5 in Type 2',
119
+ 'type': 'clinical_study',
120
+ 'year': '2022',
121
+ 'url': 'https://www.omnipod.com/onset-trial'
122
+ },
123
+ {
124
+ 'title': 'FDA 510(k) K203467 - Omnipod 5 System',
125
+ 'type': 'regulatory',
126
+ 'year': '2021',
127
+ 'url': 'https://www.accessdata.fda.gov/'
128
+ },
129
+ {
130
+ 'title': 'Omnipod 5 User Guide',
131
+ 'type': 'technical_manual',
132
+ 'year': '2021',
133
+ 'url': 'https://www.omnipod.com/'
134
+ }
135
+ ]
136
+
137
+ def set_activity_mode(self, enabled: bool, mode_type: str = 'exercise'):
138
+ """Enable activity or sleep mode"""
139
+ if mode_type == 'exercise':
140
+ self.behavior.target_glucose = 140.0 if enabled else 110.0
141
+ elif mode_type == 'sleep':
142
+ self.behavior.target_glucose = 130.0 if enabled else 110.0
143
+
144
+ if self.behavior.pid_parameters:
145
+ self.behavior.pid_parameters.target_glucose = self.behavior.target_glucose
146
+
147
+ print(f" {mode_type.title()} mode: {'ON' if enabled else 'OFF'} (target: {self.behavior.target_glucose} mg/dL)")
148
+
149
+ def emulate_decision(self,
150
+ glucose: float,
151
+ velocity: float,
152
+ insulin_on_board: float,
153
+ carbs: float,
154
+ current_time: float = 0) -> EmulatorDecision:
155
+ """
156
+ Emulate Omnipod 5 decision-making for current conditions.
157
+
158
+ Omnipod 5 features:
159
+ 1. Adaptive learning (adjusts based on user response)
160
+ 2. Automatic delivery every 5 minutes
161
+ 3. Activity and sleep modes
162
+ 4. Conservative low glucose suspend
163
+
164
+ Args:
165
+ glucose: Current glucose (mg/dL)
166
+ velocity: Rate of change (mg/dL/min)
167
+ insulin_on_board: Current IOB (units)
168
+ carbs: Carbs consumed (grams)
169
+ current_time: Simulation time (minutes)
170
+
171
+ Returns:
172
+ EmulatorDecision with insulin delivery details
173
+ """
174
+ reasoning = []
175
+ safety_overrides = []
176
+ action = 'deliver'
177
+ insulin_delivered = 0.0
178
+ pid = self.behavior.pid_parameters
179
+ safety = self.behavior.safety_limits
180
+
181
+ # Omnipod uses slightly different low threshold
182
+ low_threshold = safety.low_suspend_threshold # 67 mg/dL
183
+
184
+ # Check safety constraints
185
+ should_suspend, suspend_reasons, adjustment = self._check_safety_constraints(
186
+ glucose, velocity, insulin_on_board, carbs, current_time
187
+ )
188
+ reasoning.extend(suspend_reasons)
189
+
190
+ # Omnipod-specific low threshold
191
+ if glucose < low_threshold:
192
+ should_suspend = True
193
+ reasoning.append(
194
+ f"Omnipod low suspend: glucose {glucose:.0f} < {low_threshold:.0f} mg/dL"
195
+ )
196
+ safety_overrides.append("Omnipod proprietary low threshold")
197
+
198
+ if should_suspend:
199
+ action = 'suspend'
200
+ insulin_delivered = 0.0
201
+ safety_overrides.extend(suspend_reasons)
202
+ else:
203
+ # Calculate correction with Omnipod's adaptive approach
204
+ error = glucose - pid.target_glucose
205
+
206
+ # Adaptive PID (simplified)
207
+ self.state['integral_term'] += error * 0.04
208
+ self.state['integral_term'] = max(-pid.integral_limit,
209
+ min(pid.integral_limit,
210
+ self.state['integral_term']))
211
+
212
+ # Meal bolus
213
+ meal_bolus = 0.0
214
+ if carbs > 0:
215
+ meal_bolus = carbs / self.behavior.carb_ratio
216
+ reasoning.append(
217
+ f"Meal bolus: {carbs:.0f}g → {meal_bolus:.2f} U"
218
+ )
219
+
220
+ # Calculate correction
221
+ pid_output = (
222
+ pid.kp * error +
223
+ pid.ki * self.state['integral_term'] +
224
+ pid.kd * velocity
225
+ ) / self.behavior.correction_factor
226
+
227
+ # Apply adaptive learning (simplified)
228
+ if self._learning_model['patterns_learned'] > 10:
229
+ adaptation_factor = 1.0 + (
230
+ (50.0 - self._learning_model['user_sensitivity']) / 100
231
+ )
232
+ pid_output *= adaptation_factor
233
+
234
+ insulin_delivered = max(0, pid_output + meal_bolus) * adjustment
235
+
236
+ # Omnipod max delivery
237
+ insulin_delivered = min(insulin_delivered, safety.max_bolus)
238
+
239
+ if insulin_delivered > 0:
240
+ reasoning.append(
241
+ f"Omnipod correction: error={error:.0f} → {insulin_delivered:.2f} U"
242
+ )
243
+
244
+ # Simulate learning
245
+ self._learning_model['patterns_learned'] += 1
246
+ else:
247
+ reasoning.append("No delivery needed")
248
+
249
+ # Update cumulative insulin
250
+ self.state['cumulative_insulin'] += insulin_delivered
251
+ if self.state['cumulative_insulin'] > safety.max_daily_total:
252
+ safety_overrides.append("Daily total limit reached")
253
+ insulin_delivered = 0.0
254
+ action = 'suspend'
255
+
256
+ # Create decision
257
+ decision = EmulatorDecision(
258
+ insulin_delivered=insulin_delivered,
259
+ action=action,
260
+ reasoning=reasoning,
261
+ safety_overrides=safety_overrides,
262
+ predicted_glucose=glucose + velocity * 30,
263
+ confidence=0.87
264
+ )
265
+
266
+ self._decision_history.append(decision)
267
+
268
+ return decision
269
+
270
+ def get_algorithm_personality(self) -> Dict:
271
+ """
272
+ Get the algorithm's "personality" characteristics.
273
+
274
+ Returns:
275
+ Dictionary with personality traits
276
+ """
277
+ return {
278
+ 'name': 'Omnipod 5 Horizon',
279
+ 'type': 'Adaptive Hybrid Closed-Loop',
280
+ 'personality': {
281
+ 'aggressiveness': 'Adaptive (starts conservative)',
282
+ 'hypo_aversion': 'High (67 mg/dL threshold)',
283
+ 'response_speed': 'Moderate',
284
+ 'correction_aggressiveness': 'Adaptive',
285
+ 'meal_handling': 'Automatic delivery',
286
+ 'predictive_features': 'Adaptive learning, Activity modes'
287
+ },
288
+ 'key_differences': [
289
+ 'Tubeless patch pump design',
290
+ 'Adaptive algorithm learns user patterns',
291
+ 'Conservative low threshold (67 mg/dL)',
292
+ 'Activity & Sleep modes',
293
+ 'Longer insulin duration (5 hours)'
294
+ ],
295
+ 'limitations': [
296
+ 'Pod life limited to 72-80 hours',
297
+ 'Requires different set for exercise',
298
+ 'Learning period needed for best results'
299
+ ]
300
+ }
301
+
302
+
303
+ def demo_omnipod5():
304
+ """Demonstrate Omnipod 5 emulator"""
305
+ print("=" * 70)
306
+ print("OMNIPOD 5 EMULATOR DEMONSTRATION")
307
+ print("=" * 70)
308
+
309
+ emulator = Omnipod5Emulator()
310
+
311
+ # Print behavior profile
312
+ print("\n Algorithm Personality:")
313
+ personality = emulator.get_algorithm_personality()
314
+ print(f" Name: {personality['name']}")
315
+ print(f" Type: {personality['type']}")
316
+ print(f" Hypo Aversion: {personality['personality']['hypo_aversion']}")
317
+
318
+ # Print sources
319
+ print("\n Sources:")
320
+ for source in emulator.get_sources():
321
+ print(f" - [{source['type']}] {source['title']} ({source['year']})")
322
+
323
+ # Simulate scenarios
324
+ print("\n🧪 Scenario Simulation:")
325
+ print("-" * 50)
326
+
327
+ scenarios = [
328
+ {'glucose': 170, 'velocity': 1.0, 'iob': 1.0, 'carbs': 0, 'desc': 'Elevated glucose'},
329
+ {'glucose': 110, 'velocity': 0.2, 'iob': 1.5, 'carbs': 0, 'desc': 'At target'},
330
+ {'glucose': 67, 'velocity': -0.5, 'iob': 1.0, 'carbs': 0, 'desc': 'At Omnipod low threshold'},
331
+ {'glucose': 250, 'velocity': 2.0, 'iob': 0.5, 'carbs': 50, 'desc': 'High with large meal'},
332
+ ]
333
+
334
+ for i, scenario in enumerate(scenarios, 1):
335
+ decision = emulator.emulate_decision(
336
+ glucose=scenario['glucose'],
337
+ velocity=scenario['velocity'],
338
+ insulin_on_board=scenario['iob'],
339
+ carbs=scenario['carbs'],
340
+ current_time=i * 5
341
+ )
342
+
343
+ print(f"\n Scenario {i}: {scenario['desc']}")
344
+ print(f" Glucose: {scenario['glucose']} mg/dL, Velocity: {scenario['velocity']} mg/dL/min")
345
+ print(f" → Action: {decision.action}, Insulin: {decision.insulin_delivered:.2f} U")
346
+ print(f" Reasoning: {', '.join(decision.reasoning[:2])}")
347
+
348
+ # Test activity mode
349
+ print("\n Activity Mode Test:")
350
+ print("-" * 50)
351
+ emulator.set_activity_mode(True, 'exercise')
352
+
353
+ decision = emulator.emulate_decision(
354
+ glucose=160, velocity=0.5, insulin_on_board=1.0, carbs=0, current_time=0
355
+ )
356
+ print(f" Exercise mode: glucose=160 → {decision.insulin_delivered:.2f} U")
357
+
358
+ emulator.set_activity_mode(False)
359
+
360
+ print("\n" + "=" * 70)
361
+ print("OMNIPOD 5 EMULATOR DEMONSTRATION COMPLETE")
362
+ print("=" * 70)
363
+
364
+
365
+ if __name__ == "__main__":
366
+ demo_omnipod5()
367
+