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,393 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tandem Control-IQ Emulator - IINTS-AF
4
+ Emulates the Tandem t:slim X2 with Control-IQ algorithm.
5
+
6
+ Based on:
7
+ - Brown et al. (2019) Diabetes Technology & Therapeutics
8
+ - FDA 510(k) clearance documentation
9
+ - Clinical trial results (iDCL Trial)
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 TandemControlIQBehavior(PumpBehavior):
26
+ """Tandem Control-IQ specific behavior profile"""
27
+ # Control-IQ specific parameters
28
+ exercise_mode: bool = False
29
+ target_glucose_day: float = 112.5 # mg/dL
30
+ target_glucose_exercise: float = 140.0 # mg/dL (when exercise mode on)
31
+ max_delivery: float = 3.0 # units/hour maximum auto delivery
32
+ correction_divisor: float = 30.0 # mg/dL per unit for corrections
33
+ basal_limit: float = 2.0 # units/hour max basal override
34
+ prediction_horizon: int = 30 # minutes
35
+ prediction_window: int = 60 # minutes for activity
36
+ low_limit: float = 70.0 # mg/dL low glucose limit
37
+ high_limit: float = 180.0 # mg/dL high glucose limit
38
+ time_step_minutes: float = 5.0 # Explicitly define the decision interval
39
+
40
+ def __init__(self):
41
+ super().__init__(
42
+ pump_name="Tandem t:slim X2 Control-IQ",
43
+ manufacturer="Tandem Diabetes Care",
44
+ safety_limits=SafetyLimits(
45
+ low_suspend_threshold=70.0,
46
+ high_suspend_threshold=250.0,
47
+ max_bolus=15.0,
48
+ max_basal_rate=6.0,
49
+ max_daily_total=100.0,
50
+ auto_off_duration=60.0,
51
+ low_suspend_duration=5.0,
52
+ target_glucose=112.5
53
+ ),
54
+ pid_parameters=PIDParameters(
55
+ kp=0.03, # Proportional gain (Control-IQ tuned)
56
+ ki=0.002, # Integral gain (lower = less accumulation)
57
+ kd=0.04, # Derivative gain
58
+ target_glucose=112.5,
59
+ integral_limit=3.0
60
+ ),
61
+ correction_factor=30.0,
62
+ carb_ratio=10.0,
63
+ insulin_sensitivity_factor=45.0,
64
+ active_insulin_duration=6.0, # Control-IQ uses 6-hour DIA
65
+ safety_level=SafetyLevel.MODERATE
66
+ )
67
+
68
+
69
+ class TandemControlIQEmulator(LegacyEmulator):
70
+ """
71
+ Emulates Tandem Control-IQ algorithm.
72
+
73
+ Control-IQ is a hybrid closed-loop system that features:
74
+ - Predictive Low Glucose Suspend (PLGS)
75
+ - Predictive High Glucose Assist
76
+ - Exercise Mode (higher target)
77
+ - Automatic correction boluses
78
+ - Basal rate override
79
+
80
+ Key characteristics:
81
+ - More aggressive than Medtronic 780G
82
+ - Uses 6-hour insulin duration (vs 4-hour typical)
83
+ - Targets 112.5 mg/dL (lower than 780G's 120)
84
+ - Automatic correction every hour
85
+
86
+ Sources:
87
+ - Brown et al. (2019) "Control-IQ Technology"
88
+ - FDA 510(k) K191289
89
+ - iDCL Trial Results
90
+ """
91
+
92
+ def __init__(self):
93
+ """Initialize the Control-IQ emulator"""
94
+ super().__init__()
95
+ self.behavior = TandemControlIQBehavior()
96
+
97
+ def _get_default_behavior(self) -> TandemControlIQBehavior:
98
+ """Get Control-IQ default behavior"""
99
+ return TandemControlIQBehavior()
100
+
101
+ def get_sources(self) -> List[Dict[str, str]]:
102
+ """Get sources for Control-IQ emulation logic"""
103
+ return [
104
+ {
105
+ 'title': 'Brown et al. - Control-IQ Technology',
106
+ 'type': 'clinical_study',
107
+ 'year': '2019',
108
+ 'url': 'https://doi.org/10.1089/dia.2019.0226'
109
+ },
110
+ {
111
+ 'title': 'FDA 510(k) K191289 - Control-IQ System',
112
+ 'type': 'regulatory',
113
+ 'year': '2019',
114
+ 'url': 'https://www.accessdata.fda.gov/'
115
+ },
116
+ {
117
+ 'title': 'iDCL Trial - International Diabetes Closed Loop',
118
+ 'type': 'clinical_trial',
119
+ 'year': '2019-2020',
120
+ 'url': 'https://clinicaltrials.gov/ct2/show/NCT03563313'
121
+ },
122
+ {
123
+ 'title': 'Control-IQ User Guide',
124
+ 'type': 'technical_manual',
125
+ 'year': '2020',
126
+ 'url': 'https://www.tandemdiabetes.com/'
127
+ }
128
+ ]
129
+
130
+ def set_exercise_mode(self, enabled: bool):
131
+ """Enable/disable exercise mode (higher target)"""
132
+ self.behavior.exercise_mode = enabled
133
+ self.behavior.target_glucose_day = (
134
+ 140.0 if enabled else 112.5
135
+ )
136
+ if self.behavior.pid_parameters:
137
+ self.behavior.pid_parameters.target_glucose = (
138
+ self.behavior.target_glucose_day
139
+ )
140
+ print(f"Exercise mode: {'ON' if enabled else 'OFF'}")
141
+
142
+ def emulate_decision(self,
143
+ glucose: float,
144
+ velocity: float,
145
+ insulin_on_board: float,
146
+ carbs: float,
147
+ current_time: float = 0) -> EmulatorDecision:
148
+ """
149
+ Emulate Control-IQ decision-making for current conditions.
150
+
151
+ Control-IQ makes decisions:
152
+ 1. Predict glucose 30-60 minutes ahead
153
+ 2. Adjust delivery based on prediction
154
+ 3. Apply PLGS if predicted < 70 mg/dL
155
+ 4. Apply PHGS (predictive high) if predicted > 180 mg/dL
156
+ 5. Automatic corrections every hour (or every 5 mins in our sim for continuous control)
157
+
158
+ Args:
159
+ glucose: Current glucose (mg/dL)
160
+ velocity: Rate of change (mg/dL/min)
161
+ insulin_on_board: Current IOB (units)
162
+ carbs: Carbs consumed (grams)
163
+ current_time: Simulation time (minutes)
164
+
165
+ Returns:
166
+ EmulatorDecision with insulin delivery details
167
+ """
168
+ reasoning = []
169
+ safety_overrides = []
170
+ action = 'deliver'
171
+ insulin_delivered = 0.0
172
+ pid = self.behavior.pid_parameters
173
+ safety = self.behavior.safety_limits
174
+
175
+ # --- Update internal state ---
176
+ time_step_minutes = self.behavior.time_step_minutes
177
+ if self.state['previous_glucose'] is not None:
178
+ self.state['velocity'] = (glucose - self.state['previous_glucose']) / time_step_minutes
179
+ else:
180
+ self.state['velocity'] = velocity
181
+ self.state['previous_glucose'] = glucose
182
+
183
+ # Get target based on exercise mode
184
+ target = (
185
+ self.behavior.target_glucose_exercise
186
+ if self.behavior.exercise_mode
187
+ else self.behavior.target_glucose_day
188
+ )
189
+
190
+ # Predict glucose
191
+ predicted_glucose_30min = glucose + self.state['velocity'] * 30
192
+ predicted_glucose_60min = glucose + self.state['velocity'] * 60
193
+
194
+ # Check safety constraints first (from LegacyEmulator base class)
195
+ should_suspend_base, suspend_reasons_base, safety_adjustment_factor_base = self._check_safety_constraints(
196
+ glucose, self.state['velocity'], insulin_on_board, carbs, current_time
197
+ )
198
+ reasoning.extend(suspend_reasons_base)
199
+
200
+ # --- Tandem Control-IQ Specific Logic ---
201
+
202
+ # 1. PLGS: Predictive Low Glucose Suspend
203
+ if predicted_glucose_30min < self.behavior.low_limit and self.state['velocity'] < 0: # Only suspend if falling
204
+ action = 'suspend_insulin'
205
+ insulin_delivered = 0.0
206
+ safety_overrides.append(
207
+ f"PLGS activated: predicted {predicted_glucose_30min:.0f} mg/dL in 30 min. Insulin suspended."
208
+ )
209
+ reasoning.append(safety_overrides[-1])
210
+
211
+ # If not suspended, proceed with insulin calculation
212
+ if action != 'suspend_insulin' and not should_suspend_base: # Ensure not suspended by base safety either
213
+
214
+ # --- Range-based Target Adjustment ---
215
+ # Control-IQ dynamically adjusts its "target" based on current and predicted glucose.
216
+ # For simplicity in emulation, we'll primarily use the fixed target (112.5 or 140 for exercise),
217
+ # but then layer on range-specific corrections.
218
+
219
+ current_target = target # Base target
220
+
221
+ # --- Auto-Correction Bolus (PID based) ---
222
+ error = glucose - current_target # Error relative to current target
223
+
224
+ # Update integral term (cumulative error over time)
225
+ self.state['integral_term'] += error * time_step_minutes
226
+ self.state['integral_term'] = max(-pid.integral_limit,
227
+ min(pid.integral_limit,
228
+ self.state['integral_term']))
229
+
230
+ derivative_term = self.state['velocity']
231
+
232
+ pid_insulin_correction = (
233
+ pid.kp * error +
234
+ pid.ki * self.state['integral_term'] +
235
+ pid.kd * derivative_term
236
+ ) / self.behavior.correction_factor
237
+
238
+ # --- Meal Bolus ---
239
+ meal_bolus = 0.0
240
+ if carbs > 0:
241
+ meal_bolus = carbs / self.behavior.carb_ratio
242
+ reasoning.append(
243
+ f"Meal bolus: {carbs:.0f}g -> {meal_bolus:.2f} U"
244
+ )
245
+
246
+ # --- Apply Predictive High Glucose Assist (PHGS) ---
247
+ phgs_multiplier = 1.0
248
+ if predicted_glucose_60min > self.behavior.high_limit and predicted_glucose_60min > glucose: # Only if predicted high AND rising
249
+ # If predicted to be high and rising, be more aggressive
250
+ phgs_multiplier = 1.5 # Increase correction aggressiveness
251
+ reasoning.append(
252
+ f"PHGS activated: predicted {predicted_glucose_60min:.0f} mg/dL in 60 min. Increasing correction aggressiveness."
253
+ )
254
+
255
+ # --- Combine and apply internal limits ---
256
+ total_calculated_insulin = max(0, (pid_insulin_correction + meal_bolus) * phgs_multiplier)
257
+
258
+ # Apply individual safety adjustment factor from _check_safety_constraints (base class)
259
+ insulin_delivered = total_calculated_insulin * safety_adjustment_factor_base
260
+
261
+ # Control-IQ has max delivery limit per hour; scale to this decision interval.
262
+ max_per_interval = self.behavior.max_delivery * (time_step_minutes / 60.0)
263
+ insulin_delivered = min(insulin_delivered, max_per_interval)
264
+
265
+ if insulin_delivered > 0:
266
+ if meal_bolus > 0:
267
+ reasoning.append(f"Insulin delivered for meal and auto-correction: {insulin_delivered:.2f} U")
268
+ elif pid_insulin_correction > 0:
269
+ reasoning.append(f"Auto-correction bolus: {insulin_delivered:.2f} U (PID output based on glucose error and trend).")
270
+ else:
271
+ reasoning.append("No insulin delivered. Glucose at or near target, or PLGS active.")
272
+
273
+ # Check cumulative insulin limit (from LegacyEmulator base class)
274
+ self.state['cumulative_insulin'] += insulin_delivered # This should be cumulative sum of delivered_insulin
275
+ # The base class _check_safety_constraints should handle max_daily_total
276
+
277
+ # Create decision
278
+ decision = EmulatorDecision(
279
+ insulin_delivered=insulin_delivered,
280
+ action=action,
281
+ reasoning=reasoning,
282
+ safety_overrides=safety_overrides,
283
+ predicted_glucose=predicted_glucose_30min,
284
+ confidence=0.88
285
+ )
286
+
287
+ self._decision_history.append(decision)
288
+
289
+ return decision
290
+
291
+ def get_algorithm_personality(self) -> Dict:
292
+ """
293
+ Get the algorithm's "personality" characteristics.
294
+
295
+ Returns:
296
+ Dictionary with personality traits
297
+ """
298
+ return {
299
+ 'name': 'Tandem Control-IQ',
300
+ 'type': 'Hybrid Closed-Loop',
301
+ 'personality': {
302
+ 'aggressiveness': 'Moderate-Aggressive',
303
+ 'hypo_aversion': 'Moderate',
304
+ 'response_speed': 'Fast',
305
+ 'correction_aggressiveness': 'Aggressive',
306
+ 'meal_handling': 'Automatic corrections',
307
+ 'predictive_features': 'PLGS, PHGS, Exercise Mode'
308
+ },
309
+ 'key_differences': [
310
+ 'Lower target glucose (112.5 mg/dL)',
311
+ '6-hour insulin duration (vs 4-hour typical)',
312
+ 'Predictive High Glucose Assist (PHGS)',
313
+ 'Exercise mode with 140 mg/dL target',
314
+ 'More aggressive corrections than 780G'
315
+ ],
316
+ 'limitations': [
317
+ 'No auto-bolus feature',
318
+ 'Less conservative than 780G',
319
+ 'Exercise mode requires manual activation'
320
+ ]
321
+ }
322
+
323
+
324
+ def demo_tandem_controliq():
325
+ """Demonstrate Tandem Control-IQ emulator"""
326
+ print("=" * 70)
327
+ print("TANDEM CONTROL-IQ EMULATOR DEMONSTRATION")
328
+ print("=" * 70)
329
+
330
+ emulator = TandemControlIQEmulator()
331
+
332
+ # Print behavior profile
333
+ print("\nAlgorithm Personality:")
334
+ personality = emulator.get_algorithm_personality()
335
+ print(f" Name: {personality['name']}")
336
+ print(f" Type: {personality['type']}")
337
+ print(f" Aggressiveness: {personality['personality']['aggressiveness']}")
338
+ print(f" Response Speed: {personality['personality']['response_speed']}")
339
+
340
+ # Print sources
341
+ print("\nSources:")
342
+ for source in emulator.get_sources():
343
+ print(f" - [{source['type']}] {source['title']} ({source['year']})")
344
+
345
+ # Simulate scenarios
346
+ print("\nScenario Simulation:")
347
+ print("-" * 50)
348
+
349
+ scenarios = [
350
+ {'glucose': 180, 'velocity': 1.5, 'iob': 1.0, 'carbs': 0, 'desc': 'High glucose rising fast'},
351
+ {'glucose': 112, 'velocity': 0.3, 'iob': 1.5, 'carbs': 0, 'desc': 'At target with IOB'},
352
+ {'glucose': 70, 'velocity': -1.0, 'iob': 1.0, 'carbs': 0, 'desc': 'At low limit, stable'},
353
+ {'glucose': 200, 'velocity': 2.5, 'iob': 0.5, 'carbs': 40, 'desc': 'High with meal'},
354
+ {'glucose': 75, 'velocity': -2.0, 'iob': 0.5, 'carbs': 0, 'desc': 'Predicted low (PLGS)'},
355
+ ]
356
+
357
+ for i, scenario in enumerate(scenarios, 1):
358
+ decision = emulator.emulate_decision(
359
+ glucose=scenario['glucose'],
360
+ velocity=scenario['velocity'],
361
+ insulin_on_board=scenario['iob'],
362
+ carbs=scenario['carbs'],
363
+ current_time=i * 5
364
+ )
365
+
366
+ print(f"\n Scenario {i}: {scenario['desc']}")
367
+ print(f" Glucose: {scenario['glucose']} mg/dL, Velocity: {scenario['velocity']} mg/dL/min")
368
+ print(f" -> Action: {decision.action}, Insulin: {decision.insulin_delivered:.2f} U")
369
+ print(f" Reasoning: {', '.join(decision.reasoning[:2])}")
370
+
371
+ if decision.predicted_glucose:
372
+ print(f" Predicted (30min): {decision.predicted_glucose:.0f} mg/dL")
373
+
374
+ # Test exercise mode
375
+ print("\nExercise Mode Test:")
376
+ print("-" * 50)
377
+ emulator.set_exercise_mode(True)
378
+
379
+ decision = emulator.emulate_decision(
380
+ glucose=150, velocity=0.5, insulin_on_board=1.0, carbs=0, current_time=0
381
+ )
382
+ print(f" Exercise mode: glucose=150 -> {decision.insulin_delivered:.2f} U")
383
+ print(f" Reasoning: {', '.join(decision.reasoning)}")
384
+
385
+ emulator.set_exercise_mode(False)
386
+
387
+ print("\n" + "=" * 70)
388
+ print("TANDEM CONTROL-IQ EMULATOR DEMONSTRATION COMPLETE")
389
+ print("=" * 70)
390
+
391
+
392
+ if __name__ == "__main__":
393
+ demo_tandem_controliq()