iints-sdk-python35 0.1.18__tar.gz → 0.1.20__tar.gz
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_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/PKG-INFO +5 -2
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/README.md +3 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/pyproject.toml +2 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/__init__.py +3 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/cli/cli.py +59 -9
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/mock_algorithms.py +4 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/patient/models.py +4 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/patient/patient_factory.py +20 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/safety/input_validator.py +4 -4
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/safety/supervisor.py +1 -6
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/simulator.py +0 -3
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/supervisor.py +15 -15
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/highlevel.py +56 -4
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/research/__init__.py +8 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/research/config.py +15 -1
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/research/dataset.py +58 -1
- iints_sdk_python35-0.1.20/src/iints/research/losses.py +98 -0
- iints_sdk_python35-0.1.20/src/iints/research/metrics.py +105 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/research/predictor.py +0 -6
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints_sdk_python35.egg-info/PKG-INFO +5 -2
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints_sdk_python35.egg-info/SOURCES.txt +1 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints_sdk_python35.egg-info/requires.txt +1 -0
- iints_sdk_python35-0.1.18/src/iints/research/losses.py +0 -73
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/LICENSE +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/setup.cfg +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/algorithm_xray.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/baseline.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/clinical_benchmark.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/clinical_metrics.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/clinical_tir_analyzer.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/diabetes_metrics.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/edge_efficiency.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/edge_performance_monitor.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/explainability.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/explainable_ai.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/hardware_benchmark.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/metrics.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/population_report.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/reporting.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/safety_index.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/sensor_filtering.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/analysis/validator.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/api/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/api/base_algorithm.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/api/registry.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/api/template_algorithm.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/assets/iints_logo.png +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/cli/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/battle_runner.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/correction_bolus.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/discovery.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/fixed_basal_bolus.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/hybrid_algorithm.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/lstm_algorithm.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/pid_controller.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/standard_pump_algo.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/device.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/device_manager.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/devices/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/devices/models.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/patient/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/patient/bergman_model.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/patient/profile.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/safety/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/safety/config.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/simulation/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/simulation/scenario_parser.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/adapter.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/column_mapper.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/datasets.json +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/demo/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/demo/demo_cgm.csv +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/importer.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/ingestor.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/nightscout.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/quality_checker.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/registry.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/tidepool.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/universal_parser.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/clinic_safe_baseline.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/clinic_safe_hyper_challenge.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/clinic_safe_hypo_prone.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/clinic_safe_midnight.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/clinic_safe_pizza.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/clinic_safe_stress_meal.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/default_patient.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/data/virtual_patients/patient_559_config.yaml +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/emulation/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/emulation/legacy_base.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/emulation/medtronic_780g.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/emulation/omnipod_5.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/emulation/tandem_controliq.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/learning/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/learning/autonomous_optimizer.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/learning/learning_system.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/metrics.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/population/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/population/generator.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/population/runner.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/presets/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/presets/presets.json +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/scenarios/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/scenarios/generator.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/default_algorithm.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/scenarios/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/scenarios/chaos_insulin_stacking.json +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/scenarios/chaos_runaway_ai.json +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/scenarios/example_scenario.json +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/templates/scenarios/exercise_stress.json +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/utils/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/utils/plotting.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/utils/run_io.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/validation/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/validation/schemas.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/visualization/__init__.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/visualization/cockpit.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/visualization/uncertainty_cloud.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints_sdk_python35.egg-info/dependency_links.txt +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints_sdk_python35.egg-info/entry_points.txt +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints_sdk_python35.egg-info/top_level.txt +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/tests/test_bergman.py +0 -0
- {iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/tests/test_population.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iints-sdk-python35
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.20
|
|
4
4
|
Summary: A pre-clinical Edge-AI SDK for diabetes management validation.
|
|
5
5
|
Author-email: Rune Bobbaers <rune.bobbaers@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/python35/IINTS-SDK
|
|
@@ -29,6 +29,7 @@ Requires-Dist: seaborn>=0.11.0
|
|
|
29
29
|
Requires-Dist: typer[all]
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: hypothesis>=6.0.0; extra == "dev"
|
|
32
33
|
Requires-Dist: flake8; extra == "dev"
|
|
33
34
|
Requires-Dist: mypy; extra == "dev"
|
|
34
35
|
Requires-Dist: pandas-stubs; extra == "dev"
|
|
@@ -206,7 +207,9 @@ outputs = iints.run_simulation(..., predictor=predictor)
|
|
|
206
207
|
```
|
|
207
208
|
|
|
208
209
|
### Documentation
|
|
209
|
-
*
|
|
210
|
+
* PDF manual: `docs/manuals/IINTS-AF_SDK_Manual.pdf`
|
|
211
|
+
* Manual source: `docs/manuals/IINTS-AF_SDK_Manual.md`
|
|
212
|
+
* Comprehensive guide: `docs/COMPREHENSIVE_GUIDE.md`
|
|
210
213
|
* Notebook index: `examples/notebooks/README.md`
|
|
211
214
|
* Technical README: `docs/TECHNICAL_README.md`
|
|
212
215
|
* API Stability: `API_STABILITY.md`
|
|
@@ -158,7 +158,9 @@ outputs = iints.run_simulation(..., predictor=predictor)
|
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
### Documentation
|
|
161
|
-
*
|
|
161
|
+
* PDF manual: `docs/manuals/IINTS-AF_SDK_Manual.pdf`
|
|
162
|
+
* Manual source: `docs/manuals/IINTS-AF_SDK_Manual.md`
|
|
163
|
+
* Comprehensive guide: `docs/COMPREHENSIVE_GUIDE.md`
|
|
162
164
|
* Notebook index: `examples/notebooks/README.md`
|
|
163
165
|
* Technical README: `docs/TECHNICAL_README.md`
|
|
164
166
|
* API Stability: `API_STABILITY.md`
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "iints-sdk-python35"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.20"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Rune Bobbaers", email="rune.bobbaers@gmail.com" },
|
|
10
10
|
]
|
|
@@ -39,6 +39,7 @@ dependencies = [
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
40
|
dev = [
|
|
41
41
|
"pytest>=7.0.0",
|
|
42
|
+
"hypothesis>=6.0.0",
|
|
42
43
|
"flake8",
|
|
43
44
|
"mypy",
|
|
44
45
|
"pandas-stubs",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import pandas as pd # Required for type hints like pd.DataFrame
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
-
__version__ = "0.1.
|
|
6
|
+
__version__ = "0.1.20"
|
|
7
7
|
|
|
8
8
|
# Note to developers: this SDK is currently maintained by a single author.
|
|
9
9
|
# Please report bugs via GitHub issues and feel free to contribute fixes via PRs.
|
|
@@ -20,6 +20,7 @@ from .api.base_algorithm import (
|
|
|
20
20
|
# Core Simulation Components
|
|
21
21
|
from .core.simulator import Simulator, StressEvent, SimulationLimitError
|
|
22
22
|
from .core.patient.models import PatientModel
|
|
23
|
+
from .core.patient.patient_factory import PatientFactory
|
|
23
24
|
from .core.patient.profile import PatientProfile
|
|
24
25
|
try:
|
|
25
26
|
from .core.device_manager import DeviceManager
|
|
@@ -130,6 +131,7 @@ __all__ = [
|
|
|
130
131
|
"InsulinAlgorithm", "AlgorithmInput", "AlgorithmResult", "AlgorithmMetadata", "WhyLogEntry",
|
|
131
132
|
# Core
|
|
132
133
|
"Simulator", "StressEvent", "PatientModel", "DeviceManager",
|
|
134
|
+
"PatientFactory",
|
|
133
135
|
"PatientProfile",
|
|
134
136
|
"SimulationLimitError",
|
|
135
137
|
"SafetySupervisor",
|
|
@@ -226,7 +226,7 @@ def evaluate(
|
|
|
226
226
|
output_dir: Annotated[Optional[Path], typer.Option(help="Output directory")] = None,
|
|
227
227
|
max_workers: Annotated[Optional[int], typer.Option(help="Max parallel workers (default: all cores)")] = None,
|
|
228
228
|
seed: Annotated[Optional[int], typer.Option(help="Random seed for reproducibility")] = None,
|
|
229
|
-
patient_model: Annotated[str, typer.Option("--patient-model", help="Patient model
|
|
229
|
+
patient_model: Annotated[str, typer.Option("--patient-model", help="Patient model: auto, bergman, custom, simglucose")] = "auto",
|
|
230
230
|
):
|
|
231
231
|
"""
|
|
232
232
|
Run a Monte Carlo population evaluation of an algorithm.
|
|
@@ -581,6 +581,11 @@ def presets_run(
|
|
|
581
581
|
output_dir: Annotated[Optional[Path], typer.Option(help="Directory to save outputs")] = None,
|
|
582
582
|
compare_baselines: Annotated[bool, typer.Option(help="Run PID and standard pump baselines in the background")] = True,
|
|
583
583
|
seed: Annotated[Optional[int], typer.Option(help="Random seed for deterministic runs")] = None,
|
|
584
|
+
patient_model_type: Annotated[str, typer.Option("--patient-model", help="Patient model: auto, bergman, custom, simglucose")] = "auto",
|
|
585
|
+
sensor_noise_std: Annotated[Optional[float], typer.Option("--sensor-noise-std", help="CGM noise std (mg/dL)")] = None,
|
|
586
|
+
sensor_lag_minutes: Annotated[Optional[int], typer.Option("--sensor-lag-minutes", help="CGM lag (minutes)")] = None,
|
|
587
|
+
sensor_dropout_prob: Annotated[Optional[float], typer.Option("--sensor-dropout-prob", help="CGM dropout probability (0-1)")] = None,
|
|
588
|
+
sensor_bias: Annotated[Optional[float], typer.Option("--sensor-bias", help="CGM bias (mg/dL)")] = None,
|
|
584
589
|
safety_min_glucose: Annotated[Optional[float], typer.Option("--safety-min-glucose", help="Min plausible glucose (mg/dL)")] = None,
|
|
585
590
|
safety_max_glucose: Annotated[Optional[float], typer.Option("--safety-max-glucose", help="Max plausible glucose (mg/dL)")] = None,
|
|
586
591
|
safety_max_glucose_delta_per_5_min: Annotated[Optional[float], typer.Option("--safety-max-glucose-delta-per-5-min", help="Max glucose delta per 5 min (mg/dL)")] = None,
|
|
@@ -613,7 +618,7 @@ def presets_run(
|
|
|
613
618
|
try:
|
|
614
619
|
patient_config_name = preset.get("patient_config", "default_patient")
|
|
615
620
|
validated_patient_params = load_patient_config_by_name(patient_config_name).model_dump()
|
|
616
|
-
patient_model = iints.
|
|
621
|
+
patient_model = iints.PatientFactory.create_patient(patient_type=patient_model_type, **validated_patient_params)
|
|
617
622
|
except ValidationError as e:
|
|
618
623
|
console.print("[bold red]Patient config validation failed:[/bold red]")
|
|
619
624
|
for line in format_validation_error(e):
|
|
@@ -642,12 +647,32 @@ def presets_run(
|
|
|
642
647
|
critical_glucose_duration_minutes=safety_critical_glucose_duration_minutes,
|
|
643
648
|
)
|
|
644
649
|
|
|
650
|
+
sensor_model = None
|
|
651
|
+
if any(v is not None for v in (sensor_noise_std, sensor_lag_minutes, sensor_dropout_prob, sensor_bias)):
|
|
652
|
+
sensor_model = iints.SensorModel(
|
|
653
|
+
noise_std=float(sensor_noise_std or 0.0),
|
|
654
|
+
lag_minutes=int(sensor_lag_minutes or 0),
|
|
655
|
+
dropout_prob=float(sensor_dropout_prob or 0.0),
|
|
656
|
+
bias=float(sensor_bias or 0.0),
|
|
657
|
+
seed=resolved_seed,
|
|
658
|
+
)
|
|
659
|
+
elif patient_model_type == "auto":
|
|
660
|
+
sensor_model = iints.SensorModel(
|
|
661
|
+
noise_std=7.0,
|
|
662
|
+
lag_minutes=10,
|
|
663
|
+
dropout_prob=0.0,
|
|
664
|
+
bias=0.0,
|
|
665
|
+
seed=resolved_seed,
|
|
666
|
+
)
|
|
667
|
+
|
|
645
668
|
simulator_kwargs: Dict[str, Any] = {
|
|
646
669
|
"patient_model": patient_model,
|
|
647
670
|
"algorithm": algorithm_instance,
|
|
648
671
|
"time_step": time_step,
|
|
649
672
|
"safety_config": safety_config,
|
|
650
673
|
}
|
|
674
|
+
if sensor_model is not None:
|
|
675
|
+
simulator_kwargs["sensor_model"] = sensor_model
|
|
651
676
|
simulator_kwargs["seed"] = resolved_seed
|
|
652
677
|
if safety_config is None:
|
|
653
678
|
safety_config = SafetyConfig()
|
|
@@ -740,8 +765,9 @@ def presets_run(
|
|
|
740
765
|
if compare_baselines:
|
|
741
766
|
manifest_files["baseline_json"] = output_dir / "baseline" / "baseline_comparison.json"
|
|
742
767
|
manifest_files["baseline_csv"] = output_dir / "baseline" / "baseline_comparison.csv"
|
|
743
|
-
|
|
744
|
-
|
|
768
|
+
audit_summary_path = output_dir / "audit" / "audit_summary.json"
|
|
769
|
+
if audit_summary_path.exists():
|
|
770
|
+
manifest_files["audit_summary"] = audit_summary_path
|
|
745
771
|
run_manifest = build_run_manifest(output_dir, manifest_files)
|
|
746
772
|
run_manifest_path = output_dir / "run_manifest.json"
|
|
747
773
|
write_json(run_manifest_path, run_manifest)
|
|
@@ -749,9 +775,6 @@ def presets_run(
|
|
|
749
775
|
signature_path = maybe_sign_manifest(run_manifest_path)
|
|
750
776
|
if signature_path:
|
|
751
777
|
console.print(f"Run manifest signature: {signature_path}")
|
|
752
|
-
signature_path = maybe_sign_manifest(run_manifest_path)
|
|
753
|
-
if signature_path:
|
|
754
|
-
console.print(f"Run manifest signature: {signature_path}")
|
|
755
778
|
|
|
756
779
|
|
|
757
780
|
@presets_app.command("create")
|
|
@@ -981,6 +1004,11 @@ def run(
|
|
|
981
1004
|
output_dir: Annotated[Optional[Path], typer.Option(help="Directory to save simulation results")] = None,
|
|
982
1005
|
compare_baselines: Annotated[bool, typer.Option(help="Run PID and standard pump baselines in the background")] = True,
|
|
983
1006
|
seed: Annotated[Optional[int], typer.Option(help="Random seed for deterministic runs")] = None,
|
|
1007
|
+
patient_model_type: Annotated[str, typer.Option("--patient-model", help="Patient model: auto, bergman, custom, simglucose")] = "auto",
|
|
1008
|
+
sensor_noise_std: Annotated[Optional[float], typer.Option("--sensor-noise-std", help="CGM noise std (mg/dL)")] = None,
|
|
1009
|
+
sensor_lag_minutes: Annotated[Optional[int], typer.Option("--sensor-lag-minutes", help="CGM lag (minutes)")] = None,
|
|
1010
|
+
sensor_dropout_prob: Annotated[Optional[float], typer.Option("--sensor-dropout-prob", help="CGM dropout probability (0-1)")] = None,
|
|
1011
|
+
sensor_bias: Annotated[Optional[float], typer.Option("--sensor-bias", help="CGM bias (mg/dL)")] = None,
|
|
984
1012
|
safety_min_glucose: Annotated[Optional[float], typer.Option("--safety-min-glucose", help="Min plausible glucose (mg/dL)")] = None,
|
|
985
1013
|
safety_max_glucose: Annotated[Optional[float], typer.Option("--safety-max-glucose", help="Max plausible glucose (mg/dL)")] = None,
|
|
986
1014
|
safety_max_glucose_delta_per_5_min: Annotated[Optional[float], typer.Option("--safety-max-glucose-delta-per-5-min", help="Max glucose delta per 5 min (mg/dL)")] = None,
|
|
@@ -1060,8 +1088,11 @@ def run(
|
|
|
1060
1088
|
validated_patient_params = load_patient_config_by_name(patient_config_name).model_dump()
|
|
1061
1089
|
patient_label = patient_config_name
|
|
1062
1090
|
|
|
1063
|
-
patient_model = iints.
|
|
1064
|
-
console.print(
|
|
1091
|
+
patient_model = iints.PatientFactory.create_patient(patient_type=patient_model_type, **validated_patient_params)
|
|
1092
|
+
console.print(
|
|
1093
|
+
f"Using patient model: {patient_model.__class__.__name__} "
|
|
1094
|
+
f"({patient_model_type}) with config [cyan]{patient_label}[/cyan]"
|
|
1095
|
+
)
|
|
1065
1096
|
except ValidationError as e:
|
|
1066
1097
|
console.print("[bold red]Patient config validation failed:[/bold red]")
|
|
1067
1098
|
for line in format_validation_error(e):
|
|
@@ -1125,12 +1156,31 @@ def run(
|
|
|
1125
1156
|
output_dir = resolve_output_dir(output_dir, run_id)
|
|
1126
1157
|
|
|
1127
1158
|
effective_safety_config = safety_config or SafetyConfig()
|
|
1159
|
+
sensor_model = None
|
|
1160
|
+
if any(v is not None for v in (sensor_noise_std, sensor_lag_minutes, sensor_dropout_prob, sensor_bias)):
|
|
1161
|
+
sensor_model = iints.SensorModel(
|
|
1162
|
+
noise_std=float(sensor_noise_std or 0.0),
|
|
1163
|
+
lag_minutes=int(sensor_lag_minutes or 0),
|
|
1164
|
+
dropout_prob=float(sensor_dropout_prob or 0.0),
|
|
1165
|
+
bias=float(sensor_bias or 0.0),
|
|
1166
|
+
seed=resolved_seed,
|
|
1167
|
+
)
|
|
1168
|
+
elif patient_model_type == "auto":
|
|
1169
|
+
sensor_model = iints.SensorModel(
|
|
1170
|
+
noise_std=7.0,
|
|
1171
|
+
lag_minutes=10,
|
|
1172
|
+
dropout_prob=0.0,
|
|
1173
|
+
bias=0.0,
|
|
1174
|
+
seed=resolved_seed,
|
|
1175
|
+
)
|
|
1176
|
+
|
|
1128
1177
|
simulator = iints.Simulator(
|
|
1129
1178
|
patient_model=patient_model,
|
|
1130
1179
|
algorithm=algorithm_instance,
|
|
1131
1180
|
time_step=time_step,
|
|
1132
1181
|
seed=resolved_seed,
|
|
1133
1182
|
safety_config=effective_safety_config,
|
|
1183
|
+
sensor_model=sensor_model,
|
|
1134
1184
|
)
|
|
1135
1185
|
|
|
1136
1186
|
for event in stress_events:
|
{iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/algorithms/mock_algorithms.py
RENAMED
|
@@ -95,7 +95,10 @@ class RunawayAIAlgorithm(InsulinAlgorithm):
|
|
|
95
95
|
|
|
96
96
|
def predict_insulin(self, data: AlgorithmInput) -> Dict[str, Any]:
|
|
97
97
|
trend = data.glucose_trend_mgdl_min
|
|
98
|
-
|
|
98
|
+
current_glucose = data.current_glucose
|
|
99
|
+
if current_glucose is None:
|
|
100
|
+
dose = 0.0
|
|
101
|
+
elif current_glucose <= self.trigger_glucose or (trend is not None and trend < 0):
|
|
99
102
|
dose = self.max_bolus
|
|
100
103
|
else:
|
|
101
104
|
dose = 0.0
|
|
@@ -16,6 +16,7 @@ class CustomPatientModel:
|
|
|
16
16
|
def __init__(self, basal_insulin_rate: float = 0.8, insulin_sensitivity: float = 50.0,
|
|
17
17
|
carb_factor: float = 10.0, glucose_decay_rate: float = 0.002,
|
|
18
18
|
initial_glucose: float = 120.0, glucose_absorption_rate: float = 0.03,
|
|
19
|
+
basal_glucose_target: Optional[float] = None,
|
|
19
20
|
insulin_action_duration: float = 300.0, # minutes, e.g., 5 hours
|
|
20
21
|
insulin_peak_time: float = 75.0, # minutes
|
|
21
22
|
meal_mismatch_epsilon: float = 1.0, # Factor for meal mismatch
|
|
@@ -43,6 +44,7 @@ class CustomPatientModel:
|
|
|
43
44
|
self.carb_factor = carb_factor
|
|
44
45
|
self.glucose_decay_rate = glucose_decay_rate
|
|
45
46
|
self.glucose_absorption_rate = glucose_absorption_rate
|
|
47
|
+
self.basal_glucose_target = basal_glucose_target if basal_glucose_target is not None else initial_glucose
|
|
46
48
|
self.insulin_action_duration = insulin_action_duration
|
|
47
49
|
self.insulin_peak_time = insulin_peak_time
|
|
48
50
|
self.meal_mismatch_epsilon = meal_mismatch_epsilon
|
|
@@ -182,7 +184,8 @@ class CustomPatientModel:
|
|
|
182
184
|
|
|
183
185
|
|
|
184
186
|
# --- Basal metabolic glucose production/consumption (simplified) ---
|
|
185
|
-
|
|
187
|
+
# Homeostatic drift toward a basal target (prevents runaway decline)
|
|
188
|
+
basal_glucose_change = -self.glucose_decay_rate * (self.current_glucose - self.basal_glucose_target) * time_step
|
|
186
189
|
|
|
187
190
|
# --- Dawn phenomenon effect ---
|
|
188
191
|
dawn_effect = 0.0
|
{iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/patient/patient_factory.py
RENAMED
|
@@ -2,6 +2,13 @@ import numpy as np
|
|
|
2
2
|
from typing import Dict, Any, Optional
|
|
3
3
|
from .models import CustomPatientModel
|
|
4
4
|
|
|
5
|
+
try:
|
|
6
|
+
from .bergman_model import BergmanPatientModel
|
|
7
|
+
BERGMAN_AVAILABLE = True
|
|
8
|
+
except Exception:
|
|
9
|
+
BergmanPatientModel = None # type: ignore[assignment,misc]
|
|
10
|
+
BERGMAN_AVAILABLE = False
|
|
11
|
+
|
|
5
12
|
try:
|
|
6
13
|
from simglucose.simulation.env import T1DSimEnv
|
|
7
14
|
from simglucose.patient.t1dpatient import T1DPatient
|
|
@@ -25,10 +32,22 @@ class PatientFactory:
|
|
|
25
32
|
]
|
|
26
33
|
|
|
27
34
|
@staticmethod
|
|
28
|
-
def create_patient(patient_type='
|
|
35
|
+
def create_patient(patient_type='auto', patient_id=None, initial_glucose=120.0, **kwargs):
|
|
29
36
|
"""Create a patient model based on type."""
|
|
37
|
+
if patient_type == 'auto':
|
|
38
|
+
if BERGMAN_AVAILABLE and BergmanPatientModel is not None:
|
|
39
|
+
return BergmanPatientModel(initial_glucose=initial_glucose, **kwargs)
|
|
40
|
+
if SIMGLUCOSE_AVAILABLE:
|
|
41
|
+
patient_name = patient_id or PatientFactory.SIMGLUCOSE_PATIENTS[0]
|
|
42
|
+
return SimglucosePatientWrapper(patient_name, initial_glucose)
|
|
43
|
+
return CustomPatientModel(initial_glucose=initial_glucose, **kwargs)
|
|
30
44
|
if patient_type == 'custom':
|
|
31
45
|
return CustomPatientModel(initial_glucose=initial_glucose, **kwargs)
|
|
46
|
+
elif patient_type == 'bergman':
|
|
47
|
+
if not BERGMAN_AVAILABLE or BergmanPatientModel is None:
|
|
48
|
+
print("Warning: Bergman model not available, falling back to custom model")
|
|
49
|
+
return CustomPatientModel(initial_glucose=initial_glucose, **kwargs)
|
|
50
|
+
return BergmanPatientModel(initial_glucose=initial_glucose, **kwargs)
|
|
32
51
|
elif patient_type == 'simglucose':
|
|
33
52
|
if not SIMGLUCOSE_AVAILABLE:
|
|
34
53
|
print("Warning: Simglucose not available, falling back to custom model")
|
{iints_sdk_python35-0.1.18 → iints_sdk_python35-0.1.20}/src/iints/core/safety/input_validator.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Optional
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
2
|
|
|
3
3
|
from iints.core.safety.config import SafetyConfig
|
|
4
4
|
|
|
@@ -33,18 +33,18 @@ class InputValidator:
|
|
|
33
33
|
self.last_valid_glucose: Optional[float] = None
|
|
34
34
|
self.last_validation_time: Optional[float] = None
|
|
35
35
|
|
|
36
|
-
def reset(self):
|
|
36
|
+
def reset(self) -> None:
|
|
37
37
|
"""Resets the state of the validator for a new simulation."""
|
|
38
38
|
self.last_valid_glucose = None
|
|
39
39
|
self.last_validation_time = None
|
|
40
40
|
|
|
41
|
-
def get_state(self) ->
|
|
41
|
+
def get_state(self) -> Dict[str, Any]:
|
|
42
42
|
return {
|
|
43
43
|
"last_valid_glucose": self.last_valid_glucose,
|
|
44
44
|
"last_validation_time": self.last_validation_time,
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
def set_state(self, state:
|
|
47
|
+
def set_state(self, state: Dict[str, Any]) -> None:
|
|
48
48
|
self.last_valid_glucose = state.get("last_valid_glucose")
|
|
49
49
|
self.last_validation_time = state.get("last_validation_time")
|
|
50
50
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
3
|
from iints.core.safety.config import SafetyConfig
|
|
4
|
+
from iints.core.safety.input_validator import InputValidator
|
|
4
5
|
from iints.core.supervisor import IndependentSupervisor as FullSupervisor
|
|
5
6
|
|
|
6
7
|
class IndependentSupervisor(FullSupervisor):
|
|
@@ -31,9 +32,3 @@ class IndependentSupervisor(FullSupervisor):
|
|
|
31
32
|
|
|
32
33
|
# Alias for backward compatibility as the codebase migrates
|
|
33
34
|
SafetySupervisor = IndependentSupervisor
|
|
34
|
-
|
|
35
|
-
class InputValidator:
|
|
36
|
-
"""
|
|
37
|
-
Validates simulation inputs.
|
|
38
|
-
"""
|
|
39
|
-
pass
|
|
@@ -115,9 +115,6 @@ class Simulator:
|
|
|
115
115
|
self.simulation_data: List[Any] = [] # To store results
|
|
116
116
|
self.stress_events: List[StressEvent] = []
|
|
117
117
|
self.seed = seed
|
|
118
|
-
if self.seed is not None:
|
|
119
|
-
np.random.seed(self.seed) # Set numpy seed for reproducibility
|
|
120
|
-
# Potentially set other seeds here if other random modules are used (e.g., random.seed(self.seed))
|
|
121
118
|
self.safety_config = safety_config or SafetyConfig()
|
|
122
119
|
self.supervisor = IndependentSupervisor(safety_config=self.safety_config)
|
|
123
120
|
self.input_validator = InputValidator(safety_config=self.safety_config)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
-
from typing import Dict,
|
|
2
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
3
3
|
|
|
4
4
|
from iints.core.safety.config import SafetyConfig
|
|
5
5
|
from dataclasses import dataclass
|
|
@@ -42,18 +42,18 @@ class IndependentSupervisor:
|
|
|
42
42
|
Implements hard safety limits and emergency overrides.
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
|
-
def __init__(self,
|
|
46
|
-
hypoglycemia_threshold=70, # mg/dL
|
|
47
|
-
severe_hypoglycemia_threshold=54, # mg/dL
|
|
48
|
-
hyperglycemia_threshold=250, # mg/dL
|
|
49
|
-
max_insulin_per_bolus=5, # Units
|
|
50
|
-
glucose_rate_alarm=5, # mg/dL per minute
|
|
51
|
-
max_60min=3.0,
|
|
52
|
-
max_iob=4.0,
|
|
53
|
-
trend_stop
|
|
54
|
-
hypo_cutoff=70.0,
|
|
55
|
-
predicted_hypoglycemia_threshold=60.0, # mg/dL
|
|
56
|
-
predicted_hypoglycemia_horizon_minutes=30, # minutes
|
|
45
|
+
def __init__(self,
|
|
46
|
+
hypoglycemia_threshold: float = 70.0, # mg/dL
|
|
47
|
+
severe_hypoglycemia_threshold: float = 54.0, # mg/dL
|
|
48
|
+
hyperglycemia_threshold: float = 250.0, # mg/dL
|
|
49
|
+
max_insulin_per_bolus: float = 5.0, # Units
|
|
50
|
+
glucose_rate_alarm: float = 5.0, # mg/dL per minute
|
|
51
|
+
max_60min: float = 3.0, # Units per 60 minutes
|
|
52
|
+
max_iob: float = 4.0, # Units
|
|
53
|
+
trend_stop: float = -2.0, # mg/dL per minute
|
|
54
|
+
hypo_cutoff: float = 70.0, # mg/dL
|
|
55
|
+
predicted_hypoglycemia_threshold: float = 60.0, # mg/dL
|
|
56
|
+
predicted_hypoglycemia_horizon_minutes: int = 30, # minutes
|
|
57
57
|
contract_enabled: bool = True,
|
|
58
58
|
contract_glucose_threshold: float = 90.0,
|
|
59
59
|
contract_trend_threshold_mgdl_min: float = -1.0,
|
|
@@ -95,7 +95,7 @@ class IndependentSupervisor:
|
|
|
95
95
|
self.violations: List[SafetyViolation] = []
|
|
96
96
|
self.emergency_mode = False
|
|
97
97
|
self.last_iob = 0.0
|
|
98
|
-
self.dose_history: List[
|
|
98
|
+
self.dose_history: List[Tuple[float, float]] = []
|
|
99
99
|
|
|
100
100
|
def evaluate_safety(
|
|
101
101
|
self,
|
|
@@ -320,7 +320,7 @@ class IndependentSupervisor:
|
|
|
320
320
|
]
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
def reset(self):
|
|
323
|
+
def reset(self) -> None:
|
|
324
324
|
"""Reset supervisor state."""
|
|
325
325
|
self.glucose_history.clear()
|
|
326
326
|
self.violations.clear()
|
|
@@ -8,9 +8,10 @@ import pandas as pd
|
|
|
8
8
|
import yaml
|
|
9
9
|
|
|
10
10
|
from iints.api.base_algorithm import InsulinAlgorithm
|
|
11
|
-
from iints.core.patient.
|
|
11
|
+
from iints.core.patient.patient_factory import PatientFactory
|
|
12
12
|
from iints.core.patient.profile import PatientProfile
|
|
13
13
|
from iints.core.simulator import Simulator
|
|
14
|
+
from iints.core.devices.models import SensorModel
|
|
14
15
|
from iints.core.safety import SafetyConfig
|
|
15
16
|
from iints.analysis.baseline import run_baseline_comparison, write_baseline_comparison
|
|
16
17
|
from iints.analysis.reporting import ClinicalReportGenerator
|
|
@@ -62,6 +63,11 @@ def run_simulation(
|
|
|
62
63
|
algorithm: Union[InsulinAlgorithm, type],
|
|
63
64
|
scenario: Optional[Union[str, Path, Dict[str, Any]]] = None,
|
|
64
65
|
patient_config: Union[str, Path, Dict[str, Any], PatientProfile] = "default_patient",
|
|
66
|
+
patient_model_type: str = "auto",
|
|
67
|
+
sensor_noise_std: Optional[float] = None,
|
|
68
|
+
sensor_lag_minutes: Optional[int] = None,
|
|
69
|
+
sensor_dropout_prob: Optional[float] = None,
|
|
70
|
+
sensor_bias: Optional[float] = None,
|
|
65
71
|
duration_minutes: int = 720,
|
|
66
72
|
time_step: int = 5,
|
|
67
73
|
seed: Optional[int] = None,
|
|
@@ -81,12 +87,30 @@ def run_simulation(
|
|
|
81
87
|
output_path = resolve_output_dir(output_dir, run_id)
|
|
82
88
|
|
|
83
89
|
patient_params = _resolve_patient_config(patient_config)
|
|
84
|
-
patient_model =
|
|
90
|
+
patient_model = PatientFactory.create_patient(patient_type=patient_model_type, **patient_params)
|
|
85
91
|
|
|
86
92
|
scenario_payload = _resolve_scenario_payloads(scenario)
|
|
87
93
|
stress_event_payloads = scenario_payload.get("stress_events", []) if scenario_payload else []
|
|
88
94
|
effective_safety_config = safety_config or SafetyConfig()
|
|
89
95
|
|
|
96
|
+
sensor_model = None
|
|
97
|
+
if any(v is not None for v in (sensor_noise_std, sensor_lag_minutes, sensor_dropout_prob, sensor_bias)):
|
|
98
|
+
sensor_model = SensorModel(
|
|
99
|
+
noise_std=float(sensor_noise_std or 0.0),
|
|
100
|
+
lag_minutes=int(sensor_lag_minutes or 0),
|
|
101
|
+
dropout_prob=float(sensor_dropout_prob or 0.0),
|
|
102
|
+
bias=float(sensor_bias or 0.0),
|
|
103
|
+
seed=resolved_seed,
|
|
104
|
+
)
|
|
105
|
+
elif patient_model_type == "auto":
|
|
106
|
+
sensor_model = SensorModel(
|
|
107
|
+
noise_std=7.0,
|
|
108
|
+
lag_minutes=10,
|
|
109
|
+
dropout_prob=0.0,
|
|
110
|
+
bias=0.0,
|
|
111
|
+
seed=resolved_seed,
|
|
112
|
+
)
|
|
113
|
+
|
|
90
114
|
simulator = Simulator(
|
|
91
115
|
patient_model=patient_model,
|
|
92
116
|
algorithm=algorithm_instance,
|
|
@@ -94,6 +118,7 @@ def run_simulation(
|
|
|
94
118
|
seed=resolved_seed,
|
|
95
119
|
safety_config=effective_safety_config,
|
|
96
120
|
predictor=predictor,
|
|
121
|
+
sensor_model=sensor_model,
|
|
97
122
|
)
|
|
98
123
|
for event in build_stress_events(stress_event_payloads):
|
|
99
124
|
simulator.add_stress_event(event)
|
|
@@ -127,6 +152,7 @@ def run_simulation(
|
|
|
127
152
|
"metadata": algorithm_instance.get_algorithm_metadata().to_dict(),
|
|
128
153
|
},
|
|
129
154
|
"patient_config": patient_params,
|
|
155
|
+
"patient_model_type": patient_model_type,
|
|
130
156
|
"scenario": scenario_payload,
|
|
131
157
|
"duration_minutes": duration_minutes,
|
|
132
158
|
"time_step_minutes": time_step,
|
|
@@ -135,6 +161,7 @@ def run_simulation(
|
|
|
135
161
|
"export_audit": export_audit,
|
|
136
162
|
"generate_report": generate_report,
|
|
137
163
|
"safety_config": asdict(effective_safety_config),
|
|
164
|
+
"sensor_model": sensor_model.get_state() if sensor_model else None,
|
|
138
165
|
}
|
|
139
166
|
config_path = output_path / "config.json"
|
|
140
167
|
write_json(config_path, config_payload)
|
|
@@ -202,6 +229,11 @@ def run_full(
|
|
|
202
229
|
algorithm: Union[InsulinAlgorithm, type],
|
|
203
230
|
scenario: Optional[Union[str, Path, Dict[str, Any]]] = None,
|
|
204
231
|
patient_config: Union[str, Path, Dict[str, Any], PatientProfile] = "default_patient",
|
|
232
|
+
patient_model_type: str = "auto",
|
|
233
|
+
sensor_noise_std: Optional[float] = None,
|
|
234
|
+
sensor_lag_minutes: Optional[int] = None,
|
|
235
|
+
sensor_dropout_prob: Optional[float] = None,
|
|
236
|
+
sensor_bias: Optional[float] = None,
|
|
205
237
|
duration_minutes: int = 720,
|
|
206
238
|
time_step: int = 5,
|
|
207
239
|
seed: Optional[int] = None,
|
|
@@ -219,12 +251,30 @@ def run_full(
|
|
|
219
251
|
output_path = resolve_output_dir(output_dir, run_id)
|
|
220
252
|
|
|
221
253
|
patient_params = _resolve_patient_config(patient_config)
|
|
222
|
-
patient_model =
|
|
254
|
+
patient_model = PatientFactory.create_patient(patient_type=patient_model_type, **patient_params)
|
|
223
255
|
|
|
224
256
|
scenario_payload = _resolve_scenario_payloads(scenario)
|
|
225
257
|
stress_event_payloads = scenario_payload.get("stress_events", []) if scenario_payload else []
|
|
226
258
|
effective_safety_config = safety_config or SafetyConfig()
|
|
227
259
|
|
|
260
|
+
sensor_model = None
|
|
261
|
+
if any(v is not None for v in (sensor_noise_std, sensor_lag_minutes, sensor_dropout_prob, sensor_bias)):
|
|
262
|
+
sensor_model = SensorModel(
|
|
263
|
+
noise_std=float(sensor_noise_std or 0.0),
|
|
264
|
+
lag_minutes=int(sensor_lag_minutes or 0),
|
|
265
|
+
dropout_prob=float(sensor_dropout_prob or 0.0),
|
|
266
|
+
bias=float(sensor_bias or 0.0),
|
|
267
|
+
seed=resolved_seed,
|
|
268
|
+
)
|
|
269
|
+
elif patient_model_type == "auto":
|
|
270
|
+
sensor_model = SensorModel(
|
|
271
|
+
noise_std=7.0,
|
|
272
|
+
lag_minutes=10,
|
|
273
|
+
dropout_prob=0.0,
|
|
274
|
+
bias=0.0,
|
|
275
|
+
seed=resolved_seed,
|
|
276
|
+
)
|
|
277
|
+
|
|
228
278
|
simulator = Simulator(
|
|
229
279
|
patient_model=patient_model,
|
|
230
280
|
algorithm=algorithm_instance,
|
|
@@ -233,6 +283,7 @@ def run_full(
|
|
|
233
283
|
enable_profiling=enable_profiling,
|
|
234
284
|
safety_config=effective_safety_config,
|
|
235
285
|
predictor=predictor,
|
|
286
|
+
sensor_model=sensor_model,
|
|
236
287
|
)
|
|
237
288
|
for event in build_stress_events(stress_event_payloads):
|
|
238
289
|
simulator.add_stress_event(event)
|
|
@@ -274,6 +325,7 @@ def run_full(
|
|
|
274
325
|
"generate_report": True,
|
|
275
326
|
"enable_profiling": enable_profiling,
|
|
276
327
|
"safety_config": asdict(effective_safety_config),
|
|
328
|
+
"sensor_model": sensor_model.get_state() if sensor_model else None,
|
|
277
329
|
}
|
|
278
330
|
config_path = output_path / "config.json"
|
|
279
331
|
write_json(config_path, config_payload)
|
|
@@ -345,7 +397,7 @@ def run_population(
|
|
|
345
397
|
max_workers: Optional[int] = None,
|
|
346
398
|
safety_config: Optional[SafetyConfig] = None,
|
|
347
399
|
safety_weights: Optional[Dict[str, float]] = None,
|
|
348
|
-
patient_model_type: str = "
|
|
400
|
+
patient_model_type: str = "auto",
|
|
349
401
|
population_cv: Optional[Dict[str, float]] = None,
|
|
350
402
|
) -> Dict[str, Any]:
|
|
351
403
|
"""
|
|
@@ -7,9 +7,11 @@ from .dataset import (
|
|
|
7
7
|
save_parquet,
|
|
8
8
|
load_dataset,
|
|
9
9
|
save_dataset,
|
|
10
|
+
compute_dataset_lineage,
|
|
10
11
|
)
|
|
11
12
|
from .predictor import LSTMPredictor, load_predictor, PredictorService, load_predictor_service
|
|
12
|
-
from .losses import QuantileLoss, SafetyWeightedMSE
|
|
13
|
+
from .losses import QuantileLoss, SafetyWeightedMSE, BandWeightedMSE
|
|
14
|
+
from .metrics import regression_metrics, band_regression_metrics, interval_coverage_metrics
|
|
13
15
|
|
|
14
16
|
__all__ = [
|
|
15
17
|
"PredictorConfig",
|
|
@@ -21,10 +23,15 @@ __all__ = [
|
|
|
21
23
|
"save_parquet",
|
|
22
24
|
"load_dataset",
|
|
23
25
|
"save_dataset",
|
|
26
|
+
"compute_dataset_lineage",
|
|
24
27
|
"LSTMPredictor",
|
|
25
28
|
"load_predictor",
|
|
26
29
|
"PredictorService",
|
|
27
30
|
"load_predictor_service",
|
|
28
31
|
"QuantileLoss",
|
|
29
32
|
"SafetyWeightedMSE",
|
|
33
|
+
"BandWeightedMSE",
|
|
34
|
+
"regression_metrics",
|
|
35
|
+
"band_regression_metrics",
|
|
36
|
+
"interval_coverage_metrics",
|
|
30
37
|
]
|
|
@@ -52,7 +52,7 @@ class TrainingConfig:
|
|
|
52
52
|
# P3-10: Normalization strategy. Options: "zscore", "robust", "none".
|
|
53
53
|
normalization: str = "zscore"
|
|
54
54
|
|
|
55
|
-
# P3-12: Loss function. Options: "mse", "quantile".
|
|
55
|
+
# P3-12: Loss function. Options: "mse", "quantile", "safety_weighted", "band_weighted".
|
|
56
56
|
# For quantile loss, also set `quantile` (0 < q < 1).
|
|
57
57
|
loss: str = "mse"
|
|
58
58
|
quantile: Optional[float] = None # e.g. 0.9 for 90th-percentile upper bound
|
|
@@ -66,3 +66,17 @@ class TrainingConfig:
|
|
|
66
66
|
safety_weighted_low_threshold: float = 80.0
|
|
67
67
|
safety_weighted_alpha: float = 2.0
|
|
68
68
|
safety_weighted_max_weight: float = 4.0
|
|
69
|
+
|
|
70
|
+
# Band-weighted loss (penalize low/high glucose errors)
|
|
71
|
+
band_weighted_low_threshold: float = 70.0
|
|
72
|
+
band_weighted_high_threshold: float = 180.0
|
|
73
|
+
band_weighted_low_weight: float = 2.0
|
|
74
|
+
band_weighted_high_weight: float = 1.5
|
|
75
|
+
band_weighted_max_weight: float = 5.0
|
|
76
|
+
|
|
77
|
+
# Optional: pre-announced meals (clinically common for AID systems).
|
|
78
|
+
# If enabled, create `meal_announcement_feature` by shifting
|
|
79
|
+
# `meal_announcement_column` earlier by `meal_announcement_minutes`.
|
|
80
|
+
meal_announcement_minutes: Optional[float] = None
|
|
81
|
+
meal_announcement_column: str = "carb_intake_grams"
|
|
82
|
+
meal_announcement_feature: str = "meal_announcement_grams"
|