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.
- iints/__init__.py +183 -0
- iints/analysis/__init__.py +12 -0
- iints/analysis/algorithm_xray.py +387 -0
- iints/analysis/baseline.py +92 -0
- iints/analysis/clinical_benchmark.py +198 -0
- iints/analysis/clinical_metrics.py +551 -0
- iints/analysis/clinical_tir_analyzer.py +136 -0
- iints/analysis/diabetes_metrics.py +43 -0
- iints/analysis/edge_efficiency.py +33 -0
- iints/analysis/edge_performance_monitor.py +315 -0
- iints/analysis/explainability.py +94 -0
- iints/analysis/explainable_ai.py +232 -0
- iints/analysis/hardware_benchmark.py +221 -0
- iints/analysis/metrics.py +117 -0
- iints/analysis/population_report.py +188 -0
- iints/analysis/reporting.py +345 -0
- iints/analysis/safety_index.py +311 -0
- iints/analysis/sensor_filtering.py +54 -0
- iints/analysis/validator.py +273 -0
- iints/api/__init__.py +0 -0
- iints/api/base_algorithm.py +307 -0
- iints/api/registry.py +103 -0
- iints/api/template_algorithm.py +195 -0
- iints/assets/iints_logo.png +0 -0
- iints/cli/__init__.py +0 -0
- iints/cli/cli.py +2598 -0
- iints/core/__init__.py +1 -0
- iints/core/algorithms/__init__.py +0 -0
- iints/core/algorithms/battle_runner.py +138 -0
- iints/core/algorithms/correction_bolus.py +95 -0
- iints/core/algorithms/discovery.py +92 -0
- iints/core/algorithms/fixed_basal_bolus.py +58 -0
- iints/core/algorithms/hybrid_algorithm.py +92 -0
- iints/core/algorithms/lstm_algorithm.py +138 -0
- iints/core/algorithms/mock_algorithms.py +162 -0
- iints/core/algorithms/pid_controller.py +88 -0
- iints/core/algorithms/standard_pump_algo.py +64 -0
- iints/core/device.py +0 -0
- iints/core/device_manager.py +64 -0
- iints/core/devices/__init__.py +3 -0
- iints/core/devices/models.py +160 -0
- iints/core/patient/__init__.py +9 -0
- iints/core/patient/bergman_model.py +341 -0
- iints/core/patient/models.py +285 -0
- iints/core/patient/patient_factory.py +117 -0
- iints/core/patient/profile.py +41 -0
- iints/core/safety/__init__.py +12 -0
- iints/core/safety/config.py +37 -0
- iints/core/safety/input_validator.py +95 -0
- iints/core/safety/supervisor.py +39 -0
- iints/core/simulation/__init__.py +0 -0
- iints/core/simulation/scenario_parser.py +61 -0
- iints/core/simulator.py +874 -0
- iints/core/supervisor.py +367 -0
- iints/data/__init__.py +53 -0
- iints/data/adapter.py +142 -0
- iints/data/column_mapper.py +398 -0
- iints/data/datasets.json +132 -0
- iints/data/demo/__init__.py +1 -0
- iints/data/demo/demo_cgm.csv +289 -0
- iints/data/importer.py +275 -0
- iints/data/ingestor.py +162 -0
- iints/data/nightscout.py +128 -0
- iints/data/quality_checker.py +550 -0
- iints/data/registry.py +166 -0
- iints/data/tidepool.py +38 -0
- iints/data/universal_parser.py +813 -0
- iints/data/virtual_patients/clinic_safe_baseline.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_midnight.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_pizza.yaml +9 -0
- iints/data/virtual_patients/clinic_safe_stress_meal.yaml +9 -0
- iints/data/virtual_patients/default_patient.yaml +11 -0
- iints/data/virtual_patients/patient_559_config.yaml +11 -0
- iints/emulation/__init__.py +80 -0
- iints/emulation/legacy_base.py +414 -0
- iints/emulation/medtronic_780g.py +337 -0
- iints/emulation/omnipod_5.py +367 -0
- iints/emulation/tandem_controliq.py +393 -0
- iints/highlevel.py +451 -0
- iints/learning/__init__.py +3 -0
- iints/learning/autonomous_optimizer.py +194 -0
- iints/learning/learning_system.py +122 -0
- iints/metrics.py +34 -0
- iints/population/__init__.py +11 -0
- iints/population/generator.py +131 -0
- iints/population/runner.py +327 -0
- iints/presets/__init__.py +28 -0
- iints/presets/presets.json +114 -0
- iints/research/__init__.py +30 -0
- iints/research/config.py +68 -0
- iints/research/dataset.py +319 -0
- iints/research/losses.py +73 -0
- iints/research/predictor.py +329 -0
- iints/scenarios/__init__.py +3 -0
- iints/scenarios/generator.py +92 -0
- iints/templates/__init__.py +0 -0
- iints/templates/default_algorithm.py +91 -0
- iints/templates/scenarios/__init__.py +0 -0
- iints/templates/scenarios/chaos_insulin_stacking.json +29 -0
- iints/templates/scenarios/chaos_runaway_ai.json +25 -0
- iints/templates/scenarios/example_scenario.json +35 -0
- iints/templates/scenarios/exercise_stress.json +30 -0
- iints/utils/__init__.py +3 -0
- iints/utils/plotting.py +50 -0
- iints/utils/run_io.py +152 -0
- iints/validation/__init__.py +133 -0
- iints/validation/schemas.py +94 -0
- iints/visualization/__init__.py +34 -0
- iints/visualization/cockpit.py +691 -0
- iints/visualization/uncertainty_cloud.py +612 -0
- iints_sdk_python35-0.0.18.dist-info/METADATA +225 -0
- iints_sdk_python35-0.0.18.dist-info/RECORD +118 -0
- iints_sdk_python35-0.0.18.dist-info/WHEEL +5 -0
- iints_sdk_python35-0.0.18.dist-info/entry_points.txt +10 -0
- iints_sdk_python35-0.0.18.dist-info/licenses/LICENSE +28 -0
- 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()
|