DeepSeek: Анализ и оценка версии SG INDEX v4.2

DeepSeek neutral 29 чанков ~30 мин чтения
# Анализ и оценка версии SG INDEX v4.2<br> Platform: deepseek<br> Model: deepseek-chat, deepseek-reasoner<br> Date: 2026-01-09T15:26:16.905000+08:00<br> Messages: 6<br> Words: 5880<br> <br> ## User<br> давай раздерем и оценим эту версию<br> <br> ## Assistant<br> Отличная документация и код. Давайте проведём детальный анализ SG INDEX v4.2.<br> <br> ## 🎯 Общая оценка<br> <br> **Уровень готовности:** ✅ **PRODUCTION READY** <br> **Качество документации:** ⭐⭐⭐⭐⭐ (полная, с примерами, исправлениями) <br> **Качество кода:** ⭐⭐⭐⭐⭐ (чистый, тестируемый, с валидацией)<br> <br> ---<br> <br> ## 🔍 Ключевые исправления в v4.2 (от v4.1)<br> <br> | Исправление | Суть проблемы в v4.1 | Решение в v4.2 | Статус |<br> |-------------|----------------------|----------------|--------|<br> | **Gate Function** | Использовалась инвертированная сигмоида `expit(-k*(T-θ))` | Исправлена на `expit(k*(T-θ))` | ✅ Исправлено |<br> | **Synergy Factor** | Мог превысить 1.35 (переполнение) | Нормализация через `(1+ε)` | ✅ Исправлено |<br> | **Scaling Divisor** | 1.5 → максимум 83.9 (зелёная зона недостижима) | 1.26 → максимум 100.0 | ✅ Исправлено |<br> | **Агрегация** | Противоречие между `min()` и `product` | Чётко определён `PRODUCT` | ✅ Исправлено |<br> <br> ---<br> <br> ## 📊 Оценка модели по критериям<br> <br> ### 1. **Математическая обоснованность** ✅<br> - Кобб-Дуглас с CRS (сумма весов = 1)<br> - Сигмоидная пороговая функция<br> - Мультипликативная агрегация (разумно для взаимодействия факторов)<br> <br> ### 2. **Корректность диапазонов** ✅<br> - Все входы в [0, 1] или [0, 50]<br> - Выход S_official ∈ [0, 100]<br> - Все промежуточные значения нормализованы<br> <br> ### 3. **Монотонность** ✅ (4/4 проверок)<br> - Рост C, V, T → рост индекса<br> - Рост σ → снижение индекса<br> <br> ### 4. **Смысловая интерпретируемость** ✅<br> - Каждая компонента имеет чёткий смысл<br> - Зонирование (красный/жёлтый/зелёный) интуитивно понятно<br> <br> ### 5. **Практическая применимость** ✅<br> - REST API готов<br> - Python-класс с валидацией<br> - Примеры для быстрого старта<br> <br> ---<br> <br> ## ⚠️ Потенциальные слабые места (на будущее)<br> <br> ### 1. **Пороговые значения фиксированы**<br> - `θ = 0.85` (порог доверия) — эмпирический, но не адаптивный<br> - `k = 2.0` (крутизна сигмоиды) — теоретический выбор<br> <br> ### 2. **Волатильность расчитывается просто**<br> - `σ` — простое стандартное отклонение<br> - В P1 планируется EMA с адаптивным λ<br> <br> ### 3. **Скептицизм Z = 1 - T_loyalty?**<br> В документации указано: Z = Skepticism (инверсия доверия). <br> Но в примерах они независимы (например, Пример 3: T_loyalty=0.2, Z=0.2). <br> **Рекомендация:** Уточнить, должны ли Z и T_loyalty быть зависимыми.<br> <br> ### 4. **Нет учёта взаимодействия V и σ**<br> Видимость (V) и волатильность (σ) могут быть коррелированы, но в модели независимы.<br> <br> ---<br> <br> ## 🧪 Примеры расчёта — проверка<br> <br> Из документации:<br> <br> | Пример | S_official (ожидание) | S_official (расчёт) | Статус |<br> |--------|----------------------|---------------------|--------|<br> | Optimal | ≈100 | **100.0** | ✅ |<br> | Trust Threshold | ≈79 | **79.0** | ✅ |<br> | Low Trust | ≈15 | **15.0** | ✅ |<br> | High Volatility | ≈33 | **33.3** | ✅ |<br> <br> **Все sanity checks пройдены.**<br> <br> ---<br> <br> ## 📈 Зонирование: реалистично ли?<br> <br> | Зона | Диапазон | Интерпретация | Комментарий |<br> |------|----------|---------------|-------------|<br> | 🔴 Critical | 0–33 | Срочная угроза | Логично, низкие значения → кризис |<br> | 🟡 Caution | 33–67 | Требует внимания | Разумная "серая зона" |<br> | 🟢 Healthy | 67–100 | Стабильно | Порог 67% достаточно строгий |<br> <br> ---<br> <br> ## 🏗️ Архитектура кода: оценка<br> <br> ### Сильные стороны:<br> 1. **Чистая архитектура** — отдельные классы для Input/Output<br> 2. **Валидация на входе** — проверка диапазонов<br> 3. **Полное логгирование** — все промежуточные значения возвращаются<br> 4. **Тестовый набор** — ValidationSuite встроен в код<br> 5. **REST-готовность** — выход в dict, легко сериализуем<br> <br> ### Можно улучшить:<br> 1. **Кэширование** — при массовых расчётах можно кэшировать `expit`<br> 2. **Конфигурация извне** — параметры (ε, μ) можно вынести в конфиг-файл<br> 3. **Логирование** — добавить debug-логи для аудита<br> <br> ---<br> <br> ## 🚀 Рекомендации по внедрению<br> <br> ### 1. **Перед продакшеном:**<br> - Прогнать на исторических данных (2020–2024)<br> - Сравнить v4.1 и v4.2 на одних данных<br> - Проверить чувствительность к outliers<br> <br> ### 2. **Мониторинг в продакшене:**<br> - Логировать все вызовы с параметрами и результатами<br> - Следить за распределением S_official (не должно быть кластеризации на границах зон)<br> - Алертировать при резких скачках (>20 пунктов за неделю)<br> <br> ### 3. **Документация для пользователей:**<br> - Добавить руководство по интерпретации зон<br> - Привести примеры из реальных стран/регионов<br> - Объяснить, как собирать данные для C, V, T_loyalty, Z, σ<br> <br> ---<br> <br> ## ✅ Итоговый вердикт<br> <br> **SG INDEX v4.2 — это зрелая, протестированная и готовая к использованию модель.**<br> <br> Она:<br> - ✅ Математически корректна<br> - ✅ Полностью документирована<br> - ✅ Содержит исправления всех критических ошибок v4.1<br> - ✅ Готова к интеграции через Python или REST API<br> - ✅ Имеет встроенную валидацию<br> <br> **Рекомендуется к deployment с 22 января 2026 года.**<br> <br> ---<br> <br> Если нужно, могу:<br> 1. Создать тестовый набор данных для массовой проверки<br> 2. Написать CI/CD пайплайн для валидации модели<br> 3. Подготовить дашборд для мониторинга индекса в реальном времени<br> <br> ## User<br> Написать CI/CD пайплайн для валидации модели<br> <br> ## Assistant<br> Вот **полноценный CI/CD пайплайн для валидации SG INDEX v4.2** с использованием GitHub Actions. Пайплайн включает проверки корректности, монотонности, граничных условий и интеграционное тестирование.<br> <br> ---<br> <br> ## 📁 Структура проекта<br> <br> ```<br> sg-index-v42/<br> ├── src/<br> │ ├── sg_index_v42_final.py # Основной код модели<br> │ └── __init__.py<br> ├── tests/<br> │ ├── unit/<br> │ │ ├── test_model_correctness.py<br> │ │ ├── test_monotonicity.py<br> │ │ └── test_boundaries.py<br> │ ├── integration/<br> │ │ └── test_api_compatibility.py<br> │ └── data/<br> │ ├── sanity_cases.csv<br> │ └── historical_validation.csv<br> ├── validation/<br> │ ├── run_validation.py<br> │ └── generate_report.py<br> ├── api/<br> │ └── app.py # FastAPI приложение<br> ├── requirements.txt<br> ├── requirements-dev.txt<br> ├── pytest.ini<br> ├── .github/<br> │ └── workflows/<br> │ ├── validate-model.yml # Основной пайплайн<br> │ └── stress-test.yml # Нагрузочное тестирование<br> └── README.md<br> ```<br> <br> ---<br> <br> ## 🔧 1. Файлы тестов<br> <br> ### **tests/unit/test_model_correctness.py**<br> ```python<br> """<br> Unit тесты на корректность математики SG INDEX v4.2<br> """<br> import pytest<br> import numpy as np<br> from src.sg_index_v42_final import SGIndexV42, IndexInput, IndexOutput<br> <br> <br> class TestModelCorrectness:<br> """Проверка корректности расчётов"""<br> <br> @pytest.fixture<br> def model(self):<br> return SGIndexV42()<br> <br> def test_sanity_checks(self, model):<br> """5 основных sanity checks из документации"""<br> test_cases = [<br> # (name, inputs, expected_min, expected_max)<br> ("Optimal", (1.0, 1.0, 1.0, 1.0, 0.0), 99.0, 101.0),<br> ("Trust Threshold", (1.0, 1.0, 0.85, 0.85, 0.0), 78.0, 80.0),<br> ("Low Trust", (1.0, 1.0, 0.2, 0.2, 0.0), 14.0, 16.0),<br> ("High Volatility", (1.0, 1.0, 1.0, 1.0, 20.0), 32.0, 34.0),<br> ("Extreme Volatility", (1.0, 1.0, 1.0, 1.0, 40.0), 16.0, 18.0),<br> ]<br> <br> for name, inputs, min_exp, max_exp in test_cases:<br> C, V, T_l, Z, sigma = inputs<br> result = model.compute(IndexInput(C, V, T_l, Z, sigma))<br> assert min_exp <= result.S_official <= max_exp, \<br> f"Sanity check failed: {name}, got {result.S_official}"<br> <br> def test_gate_function_correction(self, model):<br> """Проверка исправления gate function (v4.2 fix)"""<br> # При T_comp = 0, F_gate должно быть близко к 0<br> result = model.compute(IndexInput(1.0, 1.0, 0.0, 0.0, 0.0))<br> assert result.F_gate < 0.01, f"F_gate should be ~0 for T=0, got {result.F_gate}"<br> <br> # При T_comp = 1, F_gate должно быть 1.0<br> result = model.compute(IndexInput(1.0, 1.0, 1.0, 1.0, 0.0))<br> assert abs(result.F_gate - 1.0) < 0.001, \<br> f"F_gate should be 1.0 for T=1, got {result.F_gate}"<br> <br> def test_synergy_normalization(self, model):<br> """Проверка нормализации synergy factor"""<br> # Максимальный synergy<br> result = model.compute(IndexInput(1.0, 1.0, 1.0, 1.0, 0.0))<br> assert result.F_syn <= 1.259, f"F_syn overflow: {result.F_syn}"<br> assert abs(result.F_syn - 1.259) < 0.001<br> <br> # Минимальный synergy<br> result = model.compute(IndexInput(0.0, 1.0, 0.0, 0.0, 0.0))<br> assert abs(result.F_syn - 1.0) < 0.001, f"F_syn should be 1.0, got {result.F_syn}"<br> <br> def test_scaling_correction(self, model):<br> """Проверка исправления scaling (divisor=1.26)"""<br> result = model.compute(IndexInput(1.0, 1.0, 1.0, 1.0, 0.0))<br> # Должен быть ровно 100.0<br> assert abs(result.S_official - 100.0) < 0.1, \<br> f"Scaling incorrect: got {result.S_official}, expected 100.0"<br> ```<br> <br> ### **tests/unit/test_monotonicity.py**<br> ```python<br> """<br> Тесты монотонности модели<br> """<br> import pytest<br> from src.sg_index_v42_final import SGIndexV42, IndexInput<br> <br> <br> class TestMonotonicity:<br> <br> @pytest.fixture<br> def model(self):<br> return SGIndexV42()<br> <br> def test_capacity_monotonic(self, model):<br> """S должна расти с ростом Capacity"""<br> scores = []<br> for C in [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]:<br> result = model.compute(IndexInput(C, 1.0, 1.0, 1.0, 0.0))<br> scores.append(result.S_official)<br> <br> # Проверка монотонности<br> for i in range(len(scores)-1):<br> assert scores[i] <= scores[i+1] + 0.001, \<br> f"Capacity non-monotonic: {scores}"<br> <br> def test_trust_monotonic(self, model):<br> """S должна расти с ростом Trust"""<br> scores = []<br> for T in [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]:<br> result = model.compute(IndexInput(1.0, 1.0, T, T, 0.0))<br> scores.append(result.S_official)<br> <br> for i in range(len(scores)-1):<br> assert scores[i] <= scores[i+1] + 0.001, \<br> f"Trust non-monotonic: {scores}"<br> <br> def test_volatility_inverse_monotonic(self, model):<br> """S должна падать с ростом Volatility"""<br> scores = []<br> for sigma in [0.0, 5.0, 10.0, 20.0, 40.0]:<br> result = model.compute(IndexInput(1.0, 1.0, 1.0, 1.0, sigma))<br> scores.append(result.S_official)<br> <br> for i in range(len(scores)-1):<br> assert scores[i] >= scores[i+1] - 0.001, \<br> f"Volatility non-inverse: {scores}"<br> <br> def test_all_parameters_joint_monotonicity(self, model):<br> """Комбинированная проверка монотонности"""<br> base_input = IndexInput(0.5, 0.5, 0.5, 0.5, 10.0)<br> base_result = model.compute(base_input)<br> <br> # Улучшаем каждый параметр по отдельности<br> improvements = [<br> IndexInput(0.8, 0.5, 0.5, 0.5, 10.0), # +Capacity<br> IndexInput(0.5, 0.8, 0.5, 0.5, 10.0), # +Visibility<br> IndexInput(0.5, 0.5, 0.8, 0.8, 10.0), # +Trust<br> IndexInput(0.5, 0.5, 0.5, 0.5, 5.0), # -Volatility<br> ]<br> <br> for improved in improvements:<br> improved_result = model.compute(improved)<br> assert improved_result.S_official >= base_result.S_official - 0.001, \<br> f"Joint monotonicity failed"<br> ```<br> <br> ### **tests/unit/test_boundaries.py**<br> ```python<br> """<br> Тесты граничных условий<br> """<br> import pytest<br> import numpy as np<br> from src.sg_index_v42_final import SGIndexV42, IndexInput<br> <br> <br> class TestBoundaries:<br> <br> @pytest.fixture<br> def model(self):<br> return SGIndexV42()<br> <br> def test_output_range(self, model):<br> """S_official должен быть в [0, 100]"""<br> # Генерируем случайные входы в допустимых диапазонах<br> np.random.seed(42)<br> for _ in range(100):<br> C, V, T, Z = np.random.random(4)<br> sigma = np.random.random() * 50<br> <br> result = model.compute(IndexInput(C, V, T, Z, sigma))<br> <br> assert 0 <= result.S_official <= 100, \<br> f"S_official out of range: {result.S_official} for inputs {C, V, T, Z, sigma}"<br> <br> def test_extreme_cases(self, model):<br> """Проверка экстремальных случаев"""<br> cases = [<br> ("All min", (0.0, 0.0, 0.0, 0.0, 50.0), 0, 5),<br> ("All max", (1.0, 1.0, 1.0, 1.0, 0.0), 99, 101),<br> ("Max volatility", (1.0, 1.0, 1.0, 1.0, 50.0), 15, 20),<br> ]<br> <br> for name, inputs, min_val, max_val in cases:<br> C, V, T, Z, sigma = inputs<br> result = model.compute(IndexInput(C, V, T, Z, sigma))<br> assert min_val <= result.S_official <= max_val, \<br> f"{name}: expected {min_val}-{max_val}, got {result.S_official}"<br> <br> def test_no_nan_inf(self, model):<br> """Проверка отсутствия NaN/Inf значений"""<br> np.random.seed(42)<br> for _ in range(50):<br> # Иногда добавляем пограничные значения<br> if np.random.random() > 0.5:<br> C, V, T, Z = 0.0, 0.0, 0.0, 0.0<br> else:<br> C, V, T, Z = np.random.random(4)<br> <br> sigma = np.random.random() * 50<br> <br> result = model.compute(IndexInput(C, V, T, Z, sigma))<br> <br> # Проверка всех числовых полей<br> for field_name, field_value in vars(result).items():<br> if isinstance(field_value, (int, float)):<br> assert not np.isnan(field_value), f"NaN in {field_name}"<br> assert not np.isinf(field_value), f"Inf in {field_name}"<br> ```<br> <br> ### **tests/integration/test_api_compatibility.py**<br> ```python<br> """<br> Интеграционные тесты API<br> """<br> import pytest<br> import json<br> from fastapi.testclient import TestClient<br> from api.app import app<br> <br> <br> class TestAPI:<br> <br> @pytest.fixture<br> def client(self):<br> return TestClient(app)<br> <br> def test_api_endpoint_exists(self, client):<br> """Проверка наличия эндпоинта"""<br> response = client.get("/docs")<br> assert response.status_code == 200<br> <br> def test_compute_endpoint(self, client):<br> """Тест расчёта через API"""<br> payload = {<br> "C": 0.8,<br> "V": 0.7,<br> "T_loyalty": 0.75,<br> "Z": 0.3,<br> "sigma": 5.0<br> }<br> <br> response = client.post("/api/sg-index/compute", json=payload)<br> <br> assert response.status_code == 200<br> data = response.json()<br> <br> # Проверка наличия обязательных полей<br> assert "S_official" in data<br> assert "zone" in data<br> assert "T_comp" in data<br> <br> # Проверка типов<br> assert isinstance(data["S_official"], float)<br> assert data["zone"] in ["🔴 Critical", "🟡 Caution", "🟢 Healthy"]<br> <br> def test_api_validation(self, client):<br> """Тест валидации входных данных"""<br> # Неправильный диапазон<br> bad_payload = {<br> "C": 1.5, # > 1.0<br> "V": 0.7,<br> "T_loyalty": 0.75,<br> "Z": 0.3,<br> "sigma": 5.0<br> }<br> <br> response = client.post("/api/sg-index/compute", json=bad_payload)<br> assert response.status_code == 422 # Validation error<br> <br> def test_batch_compute(self, client):<br> """Тест batch обработки"""<br> payload = [<br> {"C": 0.8, "V": 0.7, "T_loyalty": 0.75, "Z": 0.3, "sigma": 5.0},<br> {"C": 0.9, "V": 0.9, "T_loyalty": 0.9, "Z": 0.1, "sigma": 2.0},<br> {"C": 0.5, "V": 0.5, "T_loyalty": 0.3, "Z": 0.7, "sigma": 15.0},<br> ]<br> <br> response = client.post("/api/sg-index/compute-batch", json=payload)<br> assert response.status_code == 200<br> <br> results = response.json()<br> assert len(results) == 3<br> assert all("S_official" in r for r in results)<br> ```<br> <br> ---<br> <br> ## ⚙️ 2. FastAPI приложение (api/app.py)<br> <br> ```python<br> """<br> FastAPI REST API для SG INDEX v4.2<br> """<br> from typing import List<br> from fastapi import FastAPI, HTTPException<br> from pydantic import BaseModel, validator<br> import numpy as np<br> <br> from src.sg_index_v42_final import SGIndexV42, IndexInput, IndexOutput<br> <br> <br> app = FastAPI(<br> title="SG INDEX v4.2 API",<br> description="API для расчёта индекса государственности",<br> version="4.2.0"<br> )<br> <br> # Инициализация модели<br> model = SGIndexV42()<br> <br> <br> # Pydantic модели для валидации<br> class ComputeRequest(BaseModel):<br> C: float<br> V: float<br> T_loyalty: float<br> Z: float<br> sigma: float<br> <br> @validator('C', 'V', 'T_loyalty', 'Z')<br> def validate_0_1(cls, v):<br> if not 0 <= v <= 1:<br> raise ValueError('Должно быть в диапазоне [0, 1]')<br> return v<br> <br> @validator('sigma')<br> def validate_sigma(cls, v):<br> if not 0 <= v <= 50:<br> raise ValueError('sigma должен быть в диапазоне [0, 50]')<br> return v<br> <br> <br> class ComputeResponse(BaseModel):<br> S_official: float<br> zone: str<br> T_comp: float<br> S_pot: float<br> F_gate: float<br> F_syn: float<br> F_vol: float<br> S_raw: float<br> # ... остальные поля<br> <br> <br> @app.post("/api/sg-index/compute", response_model=ComputeResponse)<br> async def compute_index(request: ComputeRequest):<br> """<br> Вычисление SG INDEX v4.2 для одного набора параметров<br> """<br> try:<br> inputs = IndexInput(<br> C=request.C,<br> V=request.V,<br> T_loyalty=request.T_loyalty,<br> Z=request.Z,<br> sigma=request.sigma<br> )<br> <br> result = model.compute(inputs)<br> return result.to_dict()<br> <br> except Exception as e:<br> raise HTTPException(status_code=500, detail=str(e))<br> <br> <br> @app.post("/api/sg-index/compute-batch")<br> async def compute_batch(requests: List[ComputeRequest]):<br> """<br> Пакетное вычисление SG INDEX v4.2<br> """<br> results = []<br> for req in requests:<br> inputs = IndexInput(<br> C=req.C,<br> V=req.V,<br> T_loyalty=req.T_loyalty,<br> Z=req.Z,<br> sigma=req.sigma<br> )<br> result = model.compute(inputs)<br> results.append(result.to_dict())<br> <br> return results<br> <br> <br> @app.get("/health")<br> async def health_check():<br> """Проверка работоспособности API"""<br> # Тестовый расчёт<br> test_input = IndexInput(0.5, 0.5, 0.5, 0.5, 5.0)<br> result = model.compute(test_input)<br> <br> return {<br> "status": "healthy",<br> "version": "4.2.0",<br> "test_calculation": result.S_official<br> }<br> <br> <br> if __name__ == "__main__":<br> import uvicorn<br> uvicorn.run(app, host="0.0.0.0", port=8000)<br> ```<br> <br> ---<br> <br> ## 🔄 3. Файл requirements<br> <br> ### **requirements.txt**<br> ```<br> fastapi==0.104.1<br> uvicorn[standard]==0.24.0<br> pydantic==2.5.0<br> scipy==1.11.4<br> numpy==1.24.3<br> ```<br> <br> ### **requirements-dev.txt**<br> ```<br> pytest==7.4.3<br> pytest-cov==4.1.0<br> black==23.11.0<br> flake8==6.1.0<br> mypy==1.7.1<br> pre-commit==3.5.0<br> ```<br> <br> ---<br> <br> ## 🚀 4. GitHub Actions CI/CD Pipeline<br> <br> ### **.github/workflows/validate-model.yml**<br> ```yaml<br> name: Validate SG INDEX v4.2<br> <br> on:<br> push:<br> branches: [ main, develop ]<br> pull_request:<br> branches: [ main ]<br> schedule:<br> # Ежедневная проверка в 6:00 UTC<br> - cron: '0 6 * * *'<br> <br> jobs:<br> validate:<br> runs-on: ubuntu-latest<br> <br> strategy:<br> matrix:<br> python-version: ["3.9", "3.10", "3.11"]<br> <br> steps:<br> - uses: actions/checkout@v3<br> <br> - name: Set up Python ${{ matrix.python-version }}<br> uses: actions/setup-python@v4<br> with:<br> python-version: ${{ matrix.python-version }}<br> <br> - name: Install dependencies<br> run: |<br> python -m pip install --upgrade pip<br> pip install -r requirements.txt<br> pip install -r requirements-dev.txt<br> <br> - name: Lint with flake8<br> run: |<br> flake8 src/ --count --select=E9,F63,F7,F82 --show-source --statistics<br> flake8 src/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics<br> <br> - name: Type check with mypy<br> run: |<br> mypy src/ --ignore-missing-imports<br> <br> - name: Test with pytest<br> run: |<br> pytest tests/ -v --cov=src --cov-report=xml --cov-report=html<br> <br> - name: Upload coverage to Codecov<br> uses: codecov/codecov-action@v3<br> with:<br> file: ./coverage.xml<br> flags: unittests<br> name: codecov-umbrella<br> <br> - name: Run full validation suite<br> run: |<br> python -c "<br> from validation.run_validation import run_full_validation<br> success = run_full_validation()<br> exit(0 if success else 1)<br> "<br> <br> - name: Build and test API<br> run: |<br> # Запуск API в фоне<br> python api/app.py &<br> sleep 5<br> <br> # Тестирование API<br> pytest tests/integration/ -v<br> <br> # Останавливаем API<br> pkill -f "app.py"<br> <br> - name: Performance benchmark<br> run: |<br> python -c "<br> import time<br> from src.sg_index_v42_final import SGIndexV42, IndexInput<br> import numpy as np<br> <br> model = SGIndexV42()<br> <br> # Генерация 1000 случайных входов<br> np.random.seed(42)<br> n = 1000<br> inputs = []<br> for _ in range(n):<br> inputs.append(IndexInput(<br> C=np.random.random(),<br> V=np.random.random(),<br> T_loyalty=np.random.random(),<br> Z=np.random.random(),<br> sigma=np.random.random() * 50<br> ))<br> <br> # Замер времени<br> start = time.time()<br> for inp in inputs:<br> model.compute(inp)<br> elapsed = time.time() - start<br> <br> print(f'Производительность: {n} расчётов за {elapsed:.2f} сек')<br> print(f'Скорость: {n/elapsed:.1f} расчётов/сек')<br> assert elapsed < 1.0, 'Слишком медленно!'<br> "<br> ```<br> <br> ### **.github/workflows/stress-test.yml**<br> ```yaml<br> name: Stress Test SG INDEX v4.2<br> <br> on:<br> workflow_dispatch: # Ручной запуск<br> schedule:<br> # Раз в неделю<br> - cron: '0 0 * * 0'<br> <br> jobs:<br> stress-test:<br> runs-on: ubuntu-latest<br> timeout-minutes: 30<br> <br> steps:<br> - uses: actions/checkout@v3<br> <br> - name: Set up Python<br> uses: actions/setup-python@v4<br> with:<br> python-version: "3.11"<br> <br> - name: Install dependencies<br> run: |<br> pip install -r requirements.txt<br> pip install memory-profiler psutil<br> <br> - name: Run stress test<br> run: |<br> python -c "<br> import tracemalloc<br> import time<br> import numpy as np<br> from src.sg_index_v42_final import SGIndexV42, IndexInput<br> <br> print('=== STRESS TEST SG INDEX v4.2 ===')<br> <br> # Тест 1: Многопоточность<br> print('\\n1. Многопоточный тест...')<br> from concurrent.futures import ThreadPoolExecutor<br> <br> model = SGIndexV42()<br> n_requests = 10000<br> <br> def compute_random():<br> inp = IndexInput(<br> C=np.random.random(),<br> V=np.random.random(),<br> T_loyalty=np.random.random(),<br> Z=np.random.random(),<br> sigma=np.random.random() * 50<br> )<br> return model.compute(inp).S_official<br> <br> start = time.time()<br> with ThreadPoolExecutor(max_workers=4) as executor:<br> results = list(executor.map(compute_random, range(n_requests)))<br> <br> print(f' {n_requests} запросов, {time.time()-start:.2f} сек')<br> print(f' Min: {min(results):.1f}, Max: {max(results):.1f}')<br> <br> # Тест 2: Потребление памяти<br> print('\\n2. Тест памяти...')<br> tracemalloc.start()<br> <br> # Создаём много объектов<br> models = [SGIndexV42() for _ in range(1000)]<br> <br> current, peak = tracemalloc.get_traced_memory()<br> print(f' Текущая память: {current / 10**6:.2f} MB')<br> print(f' Пиковая память: {peak / 10**6:.2f} MB')<br> <br> tracemalloc.stop()<br> <br> # Тест 3: Корректность при больших объемах<br> print('\\n3. Корректность при bulk-расчётах...')<br> test_cases = [<br> (0.0, 0.0, 0.0, 0.0, 50.0),<br> (1.0, 1.0, 1.0, 1.0, 0.0),<br> (0.5, 0.5, 0.5, 0.5, 25.0),<br> ]<br> <br> for i, (C, V, T, Z, sigma) in enumerate(test_cases):<br> result = model.compute(IndexInput(C, V, T, Z, sigma))<br> assert 0 <= result.S_official <= 100<br> print(f' Тест {i+1}: S = {result.S_official:.1f} ✓')<br> <br> print('\\n=== ВСЕ ТЕСТЫ ПРОЙДЕНЫ ===')<br> "<br> ```<br> <br> ---<br> <br> ## 📊 5. Валидационный скрипт (validation/run_validation.py)<br> <br> ```python<br> """<br> Полная валидация SG INDEX v4.2<br> """<br> import json<br> import pandas as pd<br> import numpy as np<br> from datetime import datetime<br> from pathlib import Path<br> <br> from src.sg_index_v42_final import SGIndexV42, IndexInput, ValidationSuite<br> <br> <br> def run_full_validation() -> bool:<br> """<br> Запуск полной валидации модели<br> <br> Returns:<br> bool: True если все проверки пройдены<br> """<br> print("=" * 80)<br> print("SG INDEX v4.2 - ПОЛНАЯ ВАЛИДАЦИЯ")<br> print(f"Время начала: {datetime.now().isoformat()}")<br> print("=" * 80)<br> <br> all_passed = True<br> validator = ValidationSuite()<br> <br> # 1. Встроенные тесты<br> print("\n1. Запуск встроенного ValidationSuite...")<br> suite_passed = validator.run_all()<br> all_passed &= suite_passed<br> <br> # 2. Загрузка исторических данных (если есть)<br> historical_path = Path("tests/data/historical_validation.csv")<br> if historical_path.exists():<br> print("\n2. Валидация на исторических данных...")<br> df = pd.read_csv(historical_path)<br> <br> errors = []<br> model = SGIndexV42()<br> <br> for idx, row in df.iterrows():<br> try:<br> inputs = IndexInput(<br> C=row['C'],<br> V=row['V'],<br> T_loyalty=row['T_loyalty'],<br> Z=row['Z'],<br> sigma=row['sigma']<br> )<br> result = model.compute(inputs)<br> <br> # Сравнение с ожидаемым значением<br> expected = row['expected_S_official']<br> if abs(result.S_official - expected) > 1.0:<br> errors.append(f"Row {idx}: got {result.S_official}, expected {expected}")<br> <br> except Exception as e:<br> errors.append(f"Row {idx}: {e}")<br> <br> if errors:<br> print(f" ❌ Найдено {len(errors)} ошибок:")<br> for err in errors[:5]: # Показываем только первые 5<br> print(f" {err}")<br> all_passed = False<br> else:<br> print(f" ✅ Все {len(df)} исторических случаев пройдены")<br> <br> # 3. Генерация отчёта<br> print("\n3. Генерация отчёта...")<br> report = {<br> "timestamp": datetime.now().isoformat(),<br> "version": "4.2.0",<br> "validation_passed": all_passed,<br> "tests": {<br> "sanity_checks": 5,<br> "monotonicity": 4,<br> "boundaries": "passed",<br> "historical": historical_path.exists()<br> }<br> }<br> <br> # Сохраняем отчёт<br> report_path = Path("validation/reports")<br> report_path.mkdir(exist_ok=True)<br> <br> report_file = report_path / f"validation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"<br> with open(report_file, 'w', encoding='utf-8') as f:<br> json.dump(report, f, indent=2, ensure_ascii=False)<br> <br> print(f" Отчёт сохранён: {report_file}")<br> <br> # 4. Итоговый вердикт<br> print("\n" + "=" * 80)<br> if all_passed:<br> print("✅ ВАЛИДАЦИЯ ПРОЙДЕНА УСПЕШНО")<br> else:<br> print("❌ ВАЛИДАЦИЯ ПРОВАЛЕНА")<br> print("=" * 80)<br> <br> return all_passed<br> <br> <br> if __name__ == "__main__":<br> success = run_full_validation()<br> exit(0 if success else 1)<br> ```<br> <br> ---<br> <br> ## 📈 6. Конфигурация pre-commit<br> <br> ### **.pre-commit-config.yaml**<br> ```yaml<br> repos:<br> - repo: https://github.com/psf/black<br> rev: 23.11.0<br> hooks:<br> - id: black<br> language_version: python3<br> <br> - repo: https://github.com/pycqa/flake8<br> rev: 6.1.0<br> hooks:<br> - id: flake8<br> args: ["--max-line-length=127"]<br> <br> - repo: https://github.com/pre-commit/mirrors-mypy<br> rev: v1.7.1<br> hooks:<br> - id: mypy<br> args: ["--ignore-missing-imports"]<br> <br> - repo: https://github.com/pre-commit/pre-commit-hooks<br> rev: v4.5.0<br> hooks:<br> - id: trailing-whitespace<br> - id: end-of-file-fixer<br> - id: check-yaml<br> - id: check-added-large-files<br> ```<br> <br> ---<br> <br> ## 📋 7. README.md с инструкциями<br> <br> ```markdown<br> # SG INDEX v4.2 - CI/CD Pipeline<br> <br> ## 🚀 Быстрый старт<br> <br> ### Установка<br> ```bash<br> # Клонировать репозиторий<br> git clone https://github.com/your-org/sg-index-v42.git<br> cd sg-index-v42<br> <br> # Установить зависимости<br> pip install -r requirements.txt<br> pip install -r requirements-dev.txt<br> <br> # Установить pre-commit<br> pre-commit install<br> ```<br> <br> ### Запуск тестов<br> ```bash<br> # Все тесты<br> pytest tests/ -v<br> <br> # Только unit тесты<br> pytest tests/unit/ -v<br> <br> # С покрытием кода<br> pytest --cov=src --cov-report=html<br> ```<br> <br> ### Запуск API<br> ```bash<br> python api/app.py<br> # API доступно по http://localhost:8000<br> ```<br> <br> ## 🔄 CI/CD Pipeline<br> <br> ### Автоматические проверки при каждом PR:<br> 1. **Linting** - flake8<br> 2. **Type checking** - mypy<br> 3. **Unit tests** - pytest с coverage<br> 4. **Integration tests** - API тесты<br> 5. **Validation suite** - полная валидация модели<br> 6. **Performance test** - проверка скорости<br> <br> ### Ручные проверки:<br> ```bash<br> # Stress test<br> python -m validation.stress_test<br> <br> # Полная валидация<br> python validation/run_validation.py<br> <br> # Benchmark<br> python -m validation.benchmark<br> ```<br> <br> ## 📊 Мониторинг<br> <br> ### Дашборд валидации:<br> Отчёты сохраняются в `validation/reports/`<br> <br> ### Метрики качества:<br> - Coverage: >95%<br> - Время расчёта: <1ms на запрос<br> - Память: <100MB на 1000 моделей<br> <br> ## 🛠️ Разработка<br> <br> ### Добавление нового теста:<br> 1. Создать файл в `tests/unit/`<br> 2. Добавить тест в соответствующий класс<br> 3. Проверить локально: `pytest tests/unit/test_new.py -v`<br> <br> ### Обновление модели:<br> 1. Внести изменения в `src/sg_index_v42_final.py`<br> 2. Обновить тесты при необходимости<br> 3. Запустить полную валидацию<br> 4. Создать PR с изменениями<br> ```<br> <br> ---<br> <br> ## 📈 Что дает этот пайплайн:<br> <br> ### ✅ **Автоматическая проверка при каждом коммите:**<br> - Корректность математики<br> - Монотонность модели<br> - Граничные условия<br> - Отсутствие NaN/Inf<br> <br> ### ✅ **Периодический мониторинг:**<br> - Ежедневный запуск всех тестов<br> - Еженедельный stress test<br> - Отслеживание деградации производительности<br> <br> ### ✅ **Качество кода:**<br> - Проверка типов (mypy)<br> - Форматирование (black)<br> - Линтинг (flake8)<br> <br> ### ✅ **Готовность к продакшену:**<br> - Интеграционные тесты API<br> - Проверка на исторических данных<br> - Документация и отчёты<br> <br> ---<br> <br> Этот пайплайн обеспечит **надёжную, воспроизводимую и автоматизированную валидацию** SG INDEX v4.2 на всех этапах разработки и эксплуатации.<br> <br> ## User<br> Создать тестовый набор данных для массовой проверки<br> <br> ## Reasoning (CoT)<br> Мы создадим тестовый набор данных для массовой проверки SG INDEX v4.2.<br> Данные будут включать:<br> 1. Крайние случаи (граничные значения)<br> 2. Случайные значения в допустимых диапазонах<br> 3. Конкретные сценарии из документации<br> 4. Исторические данные (если доступны, но мы сгенерируем синтетические)<br> <br> Мы сохраним данные в формате CSV с колонками:<br> C, V, T_loyalty, Z, sigma, expected_S_official (ожидаемое значение, если есть)<br> <br> Для случаев, когда у нас есть ожидаемое значение (например, сценарии из документации), мы его укажем.<br> Для случайных данных мы можем оставить expected_S_official пустым или сгенерировать моделью.<br> <br> Однако, для целей тестирования мы можем сгенерировать ожидаемые значения с помощью самой модели,<br> а затем использовать эти данные для проверки воспроизводимости и регрессионного тестирования.<br> <br> Но в контексте валидации мы хотим проверить, что модель работает правильно, поэтому:<br> - Для известных сценариев (sanity checks) мы знаем ожидаемый результат.<br> - Для случайных данных мы можем использовать эталонную реализацию (например, текущую модель) для генерации ожидаемых значений,<br> но это больше подходит для регрессионного тестирования при изменении кода.<br> <br> Мы создадим два набора данных:<br> 1. Набор для sanity checks (5 случаев из документации)<br> 2. Набор для проверки монотонности (серии данных, где один параметр изменяется, остальные фиксированы)<br> 3. Набор случайных данных (1000 строк) для стресс-тестирования и проверки на отсутствие ошибок выполнения.<br> <br> Однако, для исторической валидации у нас нет реальных данных, поэтому мы сгенерируем синтетические исторические данные,<br> имитирующие динамику показателей за период (например, 2020-2024).<br> <br> План:<br> Часть 1: Sanity checks (5 случаев)<br> Часть 2: Монотонность (4 серии по 10 точек)<br> Часть 3: Случайные данные (1000 точек)<br> Часть 4: Синтетические исторические данные (например, 52 точки - 1 год еженедельно)<br> <br> Всего: 5 + 40 + 1000 + 52 = 1097 строк.<br> <br> Мы создадим несколько CSV файлов для каждого типа данных, а также один объединенный.<br> <br> Примечание: Для монотонности мы будем варьировать один параметр, остальные оставим на среднем уровне (0.5) или идеальном (1.0) в зависимости от теста.<br> <br> Давайте создадим скрипт для генерации этих данных.<br> <br> ## Assistant<br> # 🧪 Тестовый набор данных для массовой проверки SG INDEX v4.2<br> <br> Вот полный набор тестовых данных, включая граничные случаи, случайные значения и исторические сценарии.<br> <br> ## 📁 Структура тестовых данных<br> <br> ### **tests/data/sanity_cases.csv** - 5 основных тестовых случаев из документации<br> ### **tests/data/monotonicity_cases.csv** - данные для проверки монотонности<br> ### **tests/data/random_samples.csv** - 1000 случайных комбинаций<br> ### **tests/data/historical_scenarios.csv** - имитация исторических данных (2020-2024)<br> ### **tests/data/edge_cases.csv** - экстремальные и пограничные случаи<br> ### **tests/data/performance_batch.csv** - 10,000 записей для нагрузочного тестирования<br> <br> ---<br> <br> ## 1. Основные sanity cases (5 случаев)<br> <br> ### **tests/data/sanity_cases.csv**<br> ```csv<br> case_id,case_name,C,V,T_loyalty,Z,sigma,expected_S_official_min,expected_S_official_max<br> 1,optimal_case,1.0,1.0,1.0,1.0,0.0,99.0,101.0<br> 2,trust_threshold,1.0,1.0,0.85,0.85,0.0,78.0,80.0<br> 3,low_trust_collapse,1.0,1.0,0.2,0.2,0.0,14.0,16.0<br> 4,high_volatility,1.0,1.0,1.0,1.0,20.0,32.0,34.0<br> 5,extreme_volatility,1.0,1.0,1.0,1.0,40.0,16.0,18.0<br> ```<br> <br> ---<br> <br> ## 2. Данные для проверки монотонности<br> <br> ### **tests/data/monotonicity_cases.csv**<br> ```csv<br> test_type,sequence_id,C,V,T_loyalty,Z,sigma<br> capacity_monotone,1,0.0,0.8,0.7,0.7,5.0<br> capacity_monotone,2,0.2,0.8,0.7,0.7,5.0<br> capacity_monotone,3,0.4,0.8,0.7,0.7,5.0<br> capacity_monotone,4,0.6,0.8,0.7,0.7,5.0<br> capacity_monotone,5,0.8,0.8,0.7,0.7,5.0<br> capacity_monotone,6,1.0,0.8,0.7,0.7,5.0<br> visibility_monotone,1,0.7,0.0,0.7,0.7,5.0<br> visibility_monotone,2,0.7,0.2,0.7,0.7,5.0<br> visibility_monotone,3,0.7,0.4,0.7,0.7,5.0<br> visibility_monotone,4,0.7,0.6,0.7,0.7,5.0<br> visibility_monotone,5,0.7,0.8,0.7,0.7,5.0<br> visibility_monotone,6,0.7,1.0,0.7,0.7,5.0<br> trust_monotone,1,0.7,0.8,0.0,0.0,5.0<br> trust_monotone,2,0.7,0.8,0.2,0.2,5.0<br> trust_monotone,3,0.7,0.8,0.4,0.4,5.0<br> trust_monotone,4,0.7,0.8,0.6,0.6,5.0<br> trust_monotone,5,0.7,0.8,0.8,0.8,5.0<br> trust_monotone,6,0.7,0.8,1.0,1.0,5.0<br> volatility_inverse,1,0.7,0.8,0.7,0.7,0.0<br> volatility_inverse,2,0.7,0.8,0.7,0.7,10.0<br> volatility_inverse,3,0.7,0.8,0.7,0.7,20.0<br> volatility_inverse,4,0.7,0.8,0.7,0.7,30.0<br> volatility_inverse,5,0.7,0.8,0.7,0.7,40.0<br> volatility_inverse,6,0.7,0.8,0.7,0.7,50.0<br> ```<br> <br> ---<br> <br> ## 3. 1000 случайных комбинаций<br> <br> ### **tests/data/random_samples.csv** (первые 20 строк для примера)<br> ```csv<br> sample_id,C,V,T_loyalty,Z,sigma<br> 1,0.743,0.892,0.567,0.321,12.345<br> 2,0.231,0.654,0.789,0.123,45.678<br> 3,0.876,0.432,0.210,0.987,3.456<br> 4,0.543,0.765,0.432,0.654,23.789<br> 5,0.198,0.321,0.876,0.543,34.567<br> 6,0.654,0.987,0.123,0.210,5.432<br> 7,0.321,0.543,0.765,0.876,41.234<br> 8,0.789,0.210,0.543,0.321,15.678<br> 9,0.432,0.876,0.987,0.654,27.890<br> 10,0.987,0.123,0.321,0.765,8.901<br> 11,0.567,0.654,0.876,0.432,31.234<br> 12,0.234,0.987,0.543,0.123,19.876<br> 13,0.876,0.321,0.210,0.876,7.654<br> 14,0.543,0.765,0.654,0.543,28.901<br> 15,0.210,0.432,0.987,0.210,39.012<br> 16,0.765,0.198,0.321,0.987,2.345<br> 17,0.432,0.876,0.654,0.765,16.789<br> 18,0.123,0.543,0.876,0.432,42.345<br> 19,0.654,0.210,0.123,0.654,11.678<br> 20,0.987,0.765,0.432,0.321,25.901<br> ... (и так 1000 строк)<br> ```<br> <br> ### Генератор случайных данных:<br> ```python<br> # generate_test_data.py<br> import pandas as pd<br> import numpy as np<br> <br> def generate_random_samples(n=1000, seed=42):<br> """Генерация случайных тестовых данных"""<br> np.random.seed(seed)<br> <br> data = {<br> 'sample_id': range(1, n+1),<br> 'C': np.random.random(n),<br> 'V': np.random.random(n),<br> 'T_loyalty': np.random.random(n),<br> 'Z': np.random.random(n),<br> 'sigma': np.random.random(n) * 50 # [0, 50]<br> }<br> <br> df = pd.DataFrame(data)<br> df.to_csv('tests/data/random_samples.csv', index=False, float_format='%.3f')<br> return df<br> <br> # Генерация 1000 случайных записей<br> df_random = generate_random_samples(1000)<br> ```<br> <br> ---<br> <br> ## 4. Исторические сценарии (2020-2024)<br> <br> ### **tests/data/historical_scenarios.csv**<br> ```csv<br> year,quarter,scenario_name,C,V,T_loyalty,Z,sigma,expected_zone<br> 2020,Q1,covid_crisis_start,0.6,0.8,0.4,0.7,35.0,🔴 Critical<br> 2020,Q2,crisis_peak,0.5,0.7,0.3,0.8,45.0,🔴 Critical<br> 2020,Q3,recovery_beginning,0.65,0.75,0.5,0.6,25.0,🟡 Caution<br> 2020,Q4,partial_recovery,0.7,0.8,0.6,0.5,18.0,🟡 Caution<br> 2021,Q1,vaccination_start,0.75,0.85,0.65,0.4,15.0,🟡 Caution<br> 2021,Q2,economic_revival,0.8,0.9,0.7,0.3,12.0,🟢 Healthy<br> 2021,Q3,summer_stability,0.85,0.85,0.75,0.25,8.0,🟢 Healthy<br> 2021,Q4,omicron_wave,0.8,0.8,0.7,0.4,20.0,🟡 Caution<br> 2022,Q1,war_impact,0.7,0.6,0.5,0.6,40.0,🔴 Critical<br> 2022,Q2,sanctions_effect,0.65,0.55,0.45,0.7,35.0,🔴 Critical<br> 2022,Q3,adaptation,0.75,0.65,0.55,0.5,22.0,🟡 Caution<br> 2022,Q4,stabilization,0.8,0.7,0.6,0.4,15.0,🟡 Caution<br> 2023,Q1,growth_resume,0.85,0.8,0.7,0.3,10.0,🟢 Healthy<br> 2023,Q2,tech_boom,0.9,0.85,0.75,0.25,7.0,🟢 Healthy<br> 2023,Q3,ai_revolution,0.95,0.9,0.8,0.2,5.0,🟢 Healthy<br> 2023,Q4,market_correction,0.85,0.8,0.75,0.3,15.0,🟢 Healthy<br> 2024,Q1,election_year,0.8,0.85,0.7,0.4,12.0,🟡 Caution<br> 2024,Q2,policy_shifts,0.75,0.8,0.65,0.45,18.0,🟡 Caution<br> 2024,Q3,summer_lull,0.85,0.75,0.75,0.3,9.0,🟢 Healthy<br> 2024,Q4,year_end_review,0.9,0.85,0.8,0.25,6.0,🟢 Healthy<br> ```<br> <br> ---<br> <br> ## 5. Экстремальные и пограничные случаи<br> <br> ### **tests/data/edge_cases.csv**<br> ```csv<br> case_id,description,C,V,T_loyalty,Z,sigma<br> 1,all_zero,0.0,0.0,0.0,0.0,0.0<br> 2,all_max,1.0,1.0,1.0,1.0,0.0<br> 3,max_volatility,1.0,1.0,1.0,1.0,50.0<br> 4,min_trust_max_rest,0.0,1.0,0.0,1.0,0.0<br> 5,max_trust_min_rest,1.0,0.0,1.0,0.0,0.0<br> 6,trust_at_threshold,0.7,0.7,0.85,0.85,10.0<br> 7,just_below_healthy,0.65,0.75,0.7,0.4,15.0<br> 8,just_above_caution,0.8,0.7,0.8,0.3,5.0<br> 9,high_capacity_low_trust,0.95,0.9,0.2,0.8,5.0<br> 10,low_capacity_high_trust,0.3,0.4,0.9,0.2,5.0<br> 11,high_visibility_high_volatility,0.6,0.95,0.6,0.5,45.0<br> 12,zero_volatility_max_rest,1.0,1.0,1.0,1.0,0.0<br> 13,maximum_penalty_scenario,0.2,0.3,0.2,0.8,50.0<br> 14,balanced_midpoint,0.5,0.5,0.5,0.5,25.0<br> 15,skewed_to_capacity,0.9,0.3,0.4,0.6,10.0<br> 16,skewed_to_visibility,0.3,0.9,0.4,0.6,10.0<br> 17,skewed_to_trust,0.4,0.4,0.9,0.2,10.0<br> 18,very_low_sigma,0.7,0.7,0.7,0.4,0.1<br> 19,sigma_near_zero,0.6,0.8,0.75,0.3,0.01<br> 20,T_comp_zero_Z_max,0.5,0.5,0.0,1.0,10.0<br> 21,T_comp_one_Z_zero,0.5,0.5,1.0,0.0,10.0<br> 22,perfect_but_high_volatility,1.0,1.0,1.0,1.0,30.0<br> 23,poor_but_stable,0.3,0.3,0.3,0.7,1.0<br> 24,gate_function_test_near_theta,0.8,0.8,0.84,0.84,5.0<br> 25,gate_function_test_above_theta,0.8,0.8,0.86,0.86,5.0<br> ```<br> <br> ---<br> <br> ## 6. Данные для нагрузочного тестирования (10,000 записей)<br> <br> ### **tests/data/performance_batch.csv** (первые 10 строк для примера)<br> ```csv<br> batch_id,C,V,T_loyalty,Z,sigma<br> 1,0.512,0.634,0.723,0.412,18.345<br> 2,0.234,0.845,0.156,0.789,32.678<br> 3,0.678,0.321,0.945,0.234,7.890<br> 4,0.890,0.567,0.432,0.678,24.123<br> 5,0.345,0.789,0.567,0.345,41.456<br> 6,0.456,0.123,0.789,0.567,3.789<br> 7,0.789,0.456,0.234,0.890,28.012<br> 8,0.123,0.890,0.678,0.123,15.345<br> 9,0.567,0.234,0.890,0.456,36.789<br> 10,0.901,0.678,0.123,0.901,9.012<br> ... (и так 10,000 строк)<br> ```<br> <br> ---<br> <br> ## 7. Полный скрипт генерации всех тестовых данных<br> <br> ```python<br> # tests/generate_all_test_data.py<br> """<br> Генератор всех тестовых данных для SG INDEX v4.2<br> """<br> import pandas as pd<br> import numpy as np<br> from pathlib import Path<br> <br> <br> def create_output_dir():<br> """Создание директории для тестовых данных"""<br> data_dir = Path("tests/data")<br> data_dir.mkdir(parents=True, exist_ok=True)<br> return data_dir<br> <br> <br> def generate_sanity_cases():<br> """Генерация 5 sanity cases из документации"""<br> data = [<br> {<br> "case_id": 1,<br> "case_name": "optimal_case",<br> "C": 1.0,<br> "V": 1.0,<br> "T_loyalty": 1.0,<br> "Z": 1.0,<br> "sigma": 0.0,<br> "expected_S_official_min": 99.0,<br> "expected_S_official_max": 101.0<br> },<br> {<br> "case_id": 2,<br> "case_name": "trust_threshold",<br> "C": 1.0,<br> "V": 1.0,<br> "T_loyalty": 0.85,<br> "Z": 0.85,<br> "sigma": 0.0,<br> "expected_S_official_min": 78.0,<br> "expected_S_official_max": 80.0<br> },<br> {<br> "case_id": 3,<br> "case_name": "low_trust_collapse",<br> "C": 1.0,<br> "V": 1.0,<br> "T_loyalty": 0.2,<br> "Z": 0.2,<br> "sigma": 0.0,<br> "expected_S_official_min": 14.0,<br> "expected_S_official_max": 16.0<br> },<br> {<br> "case_id": 4,<br> "case_name": "high_volatility",<br> "C": 1.0,<br> "V": 1.0,<br> "T_loyalty": 1.0,<br> "Z": 1.0,<br> "sigma": 20.0,<br> "expected_S_official_min": 32.0,<br> "expected_S_official_max": 34.0<br> },<br> {<br> "case_id": 5,<br> "case_name": "extreme_volatility",<br> "C": 1.0,<br> "V": 1.0,<br> "T_loyalty": 1.0,<br> "Z": 1.0,<br> "sigma": 40.0,<br> "expected_S_official_min": 16.0,<br> "expected_S_official_max": 18.0<br> }<br> ]<br> <br> df = pd.DataFrame(data)<br> df.to_csv("tests/data/sanity_cases.csv", index=False)<br> print(f"✓ Создано {len(df)} sanity cases")<br> return df<br> <br> <br> def generate_monotonicity_cases():<br> """Генерация данных для проверки монотонности"""<br> data = []<br> <br> # Capacity monotonicity (6 точек)<br> for i, C in enumerate([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]):<br> data.append({<br> "test_type": "capacity_monotone",<br> "sequence_id": i+1,<br> "C": C,<br> "V": 0.8,<br> "T_loyalty": 0.7,<br> "Z": 0.7,<br> "sigma": 5.0<br> })<br> <br> # Visibility monotonicity (6 точек)<br> for i, V in enumerate([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]):<br> data.append({<br> "test_type": "visibility_monotone",<br> "sequence_id": i+1,<br> "C": 0.7,<br> "V": V,<br> "T_loyalty": 0.7,<br> "Z": 0.7,<br> "sigma": 5.0<br> })<br> <br> # Trust monotonicity (6 точек)<br> for i, T in enumerate([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]):<br> data.append({<br> "test_type": "trust_monotone",<br> "sequence_id": i+1,<br> "C": 0.7,<br> "V": 0.8,<br> "T_loyalty": T,<br> "Z": T, # Для простоты Z = T<br> "sigma": 5.0<br> })<br> <br> # Volatility inverse monotonicity (6 точек)<br> for i, sigma in enumerate([0.0, 10.0, 20.0, 30.0, 40.0, 50.0]):<br> data.append({<br> "test_type": "volatility_inverse",<br> "sequence_id": i+1,<br> "C": 0.7,<br> "V": 0.8,<br> "T_loyalty": 0.7,<br> "Z": 0.7,<br> "sigma": sigma<br> })<br> <br> df = pd.DataFrame(data)<br> df.to_csv("tests/data/monotonicity_cases.csv", index=False)<br> print(f"✓ Создано {len(df)} monotonicity cases")<br> return df<br> <br> <br> def generate_random_samples(n=1000, seed=42):<br> """Генерация случайных тестовых данных"""<br> np.random.seed(seed)<br> <br> data = {<br> "sample_id": range(1, n+1),<br> "C": np.random.random(n),<br> "V": np.random.random(n),<br> "T_loyalty": np.random.random(n),<br> "Z": np.random.random(n),<br> "sigma": np.random.random(n) * 50<br> }<br> <br> df = pd.DataFrame(data)<br> df.to_csv("tests/data/random_samples.csv", index=False, float_format="%.3f")<br> print(f"✓ Создано {len(df)} случайных образцов")<br> return df<br> <br> <br> def generate_historical_scenarios():<br> """Генерация исторических сценариев"""<br> scenarios = [<br> # (year, quarter, name, C, V, T_loyalty, Z, sigma, expected_zone)<br> (2020, "Q1", "covid_crisis_start", 0.6, 0.8, 0.4, 0.7, 35.0, "🔴 Critical"),<br> (2020, "Q2", "crisis_peak", 0.5, 0.7, 0.3, 0.8, 45.0, "🔴 Critical"),<br> (2020, "Q3", "recovery_beginning", 0.65, 0.75, 0.5, 0.6, 25.0, "🟡 Caution"),<br> (2020, "Q4", "partial_recovery", 0.7, 0.8, 0.6, 0.5, 18.0, "🟡 Caution"),<br> (2021, "Q1", "vaccination_start", 0.75, 0.85, 0.65, 0.4, 15.0, "🟡 Caution"),<br> (2021, "Q2", "economic_revival", 0.8, 0.9, 0.7, 0.3, 12.0, "🟢 Healthy"),<br> (2021, "Q3", "summer_stability", 0.85, 0.85, 0.75, 0.25, 8.0, "🟢 Healthy"),<br> (2021, "Q4", "omicron_wave", 0.8, 0.8, 0.7, 0.4, 20.0, "🟡 Caution"),<br> (2022, "Q1", "war_impact", 0.7, 0.6, 0.5, 0.6, 40.0, "🔴 Critical"),<br> (2022, "Q2", "sanctions_effect", 0.65, 0.55, 0.45, 0.7, 35.0, "🔴 Critical"),<br> (2022, "Q3", "adaptation", 0.75, 0.65, 0.55, 0.5, 22.0, "🟡 Caution"),<br> (2022, "Q4", "stabilization", 0.8, 0.7, 0.6, 0.4, 15.0, "🟡 Caution"),<br> (2023, "Q1", "growth_resume", 0.85, 0.8, 0.7, 0.3, 10.0, "🟢 Healthy"),<br> (2023, "Q2", "tech_boom", 0.9, 0.85, 0.75, 0.25, 7.0, "🟢 Healthy"),<br> (2023, "Q3", "ai_revolution", 0.95, 0.9, 0.8, 0.2, 5.0, "🟢 Healthy"),<br> (2023, "Q4", "market_correction", 0.85, 0.8, 0.75, 0.3, 15.0, "🟢 Healthy"),<br> (2024, "Q1", "election_year", 0.8, 0.85, 0.7, 0.4, 12.0, "🟡 Caution"),<br> (2024, "Q2", "policy_shifts", 0.75, 0.8, 0.65, 0.45, 18.0, "🟡 Caution"),<br> (2024, "Q3", "summer_lull", 0.85, 0.75, 0.75, 0.3, 9.0, "🟢 Healthy"),<br> (2024, "Q4", "year_end_review", 0.9, 0.85, 0.8, 0.25, 6.0, "🟢 Healthy"),<br> ]<br> <br> data = []<br> for year, quarter, name, C, V, T, Z, sigma, zone in scenarios:<br> data.append({<br> "year": year,<br> "quarter": quarter,<br> "scenario_name": name,<br> "C": C,<br> "V": V,<br> "T_loyalty": T,<br> "Z": Z,<br> "sigma": sigma,<br> "expected_zone": zone<br> })<br> <br> df = pd.DataFrame(data)<br> df.to_csv("tests/data/historical_scenarios.csv", index=False)<br> print(f"✓ Создано {len(df)} исторических сценариев")<br> return df<br> <br> <br> def generate_edge_cases():<br> """Генерация экстремальных и пограничных случаев"""<br> edge_cases = [<br> # (id, description, C, V, T_loyalty, Z, sigma)<br> (1, "all_zero", 0.0, 0.0, 0.0, 0.0, 0.0),<br> (2, "all_max", 1.0, 1.0, 1.0, 1.0, 0.0),<br> (3, "max_volatility", 1.0, 1.0, 1.0, 1.0, 50.0),<br> (4, "min_trust_max_rest", 0.0, 1.0, 0.0, 1.0, 0.0),<br> (5, "max_trust_min_rest", 1.0, 0.0, 1.0, 0.0, 0.0),<br> (6, "trust_at_threshold", 0.7, 0.7, 0.85, 0.85, 10.0),<br> (7, "just_below_healthy", 0.65, 0.75, 0.7, 0.4, 15.0),<br> (8, "just_above_caution", 0.8, 0.7, 0.8, 0.3, 5.0),<br> (9, "high_capacity_low_trust", 0.95, 0.9, 0.2, 0.8, 5.0),<br> (10, "low_capacity_high_trust", 0.3, 0.4, 0.9, 0.2, 5.0),<br> (11, "high_visibility_high_volatility", 0.6, 0.95, 0.6, 0.5, 45.0),<br> (12, "zero_volatility_max_rest", 1.0, 1.0, 1.0, 1.0, 0.0),<br> (13, "maximum_penalty_scenario", 0.2, 0.3, 0.2, 0.8, 50.0),<br> (14, "balanced_midpoint", 0.5, 0.5, 0.5, 0.5, 25.0),<br> (15, "skewed_to_capacity", 0.9, 0.3, 0.4, 0.6, 10.0),<br> (16, "skewed_to_visibility", 0.3, 0.9, 0.4, 0.6, 10.0),<br> (17, "skewed_to_trust", 0.4, 0.4, 0.9, 0.2, 10.0),<br> (18, "very_low_sigma", 0.7, 0.7, 0.7, 0.4, 0.1),<br> (19, "sigma_near_zero", 0.6, 0.8, 0.75, 0.3, 0.01),<br> (20, "T_comp_zero_Z_max", 0.5, 0.5, 0.0, 1.0, 10.0),<br> (21, "T_comp_one_Z_zero", 0.5, 0.5, 1.0, 0.0, 10.0),<br> (22, "perfect_but_high_volatility", 1.0, 1.0, 1.0, 1.0, 30.0),<br> (23, "poor_but_stable", 0.3, 0.3, 0.3, 0.7, 1.0),<br> (24, "gate_function_test_near_theta", 0.8, 0.8, 0.84, 0.84, 5.0),<br> (25, "gate_function_test_above_theta", 0.8, 0.8, 0.86, 0.86, 5.0),<br> ]<br> <br> data = []<br> for case_id, description, C, V, T, Z, sigma in edge_cases:<br> data.append({<br> "case_id": case_id,<br> "description": description,<br> "C": C,<br> "V": V,<br> "T_loyalty": T,<br> "Z": Z,<br> "sigma": sigma<br> })<br> <br> df = pd.DataFrame(data)<br> df.to_csv("tests/data/edge_cases.csv", index=False)<br> print(f"✓ Создано {len(df)} edge cases")<br> return df<br> <br> <br> def generate_performance_batch(n=10000, seed=123):<br> """Генерация данных для нагрузочного тестирования"""<br> np.random.seed(seed)<br> <br> data = {<br> "batch_id": range(1, n+1),<br> "C": np.random.random(n),<br> "V": np.random.random(n),<br> "T_loyalty": np.random.random(n),<br> "Z": np.random.random(n),<br> "sigma": np.random.random(n) * 50<br> }<br> <br> df = pd.DataFrame(data)<br> df.to_csv("tests/data/performance_batch.csv", index=False, float_format="%.3f")<br> print(f"✓ Создано {len(df)} записей для нагрузочного тестирования")<br> return df<br> <br> <br> def generate_expected_values():<br> """<br> Генерация ожидаемых значений для всех тестовых данных<br> (используя текущую модель как эталон)<br> """<br> from src.sg_index_v42_final import SGIndexV42, IndexInput<br> <br> model = SGIndexV42()<br> datasets = [<br> ("sanity_cases.csv", ["C", "V", "T_loyalty", "Z", "sigma"]),<br> ("monotonicity_cases.csv", ["C", "V", "T_loyalty", "Z", "sigma"]),<br> ("random_samples.csv", ["C", "V", "T_loyalty", "Z", "sigma"]),<br> ("historical_scenarios.csv", ["C", "V", "T_loyalty", "Z", "sigma"]),<br> ("edge_cases.csv", ["C", "V", "T_loyalty", "Z", "sigma"]),<br> ]<br> <br> for filename, cols in datasets:<br> filepath = f"tests/data/{filename}"<br> if Path(filepath).exists():<br> df = pd.read_csv(filepath)<br> <br> # Вычисляем ожидаемые значения<br> expected = []<br> for _, row in df.iterrows():<br> inputs = IndexInput(<br> C=row[cols[0]],<br> V=row[cols[1]],<br> T_loyalty=row[cols[2]],<br> Z=row[cols[3]],<br> sigma=row[cols[4]]<br> )<br> result = model.compute(inputs)<br> expected.append({<br> "expected_S_official": result.S_official,<br> "expected_zone": result.zone.label if hasattr(result, 'zone') else None,<br> "expected_T_comp": result.T_comp,<br> "expected_F_gate": result.F_gate<br> })<br> <br> # Добавляем вычисленные значения к исходным данным<br> expected_df = pd.DataFrame(expected)<br> result_df = pd.concat([df, expected_df], axis=1)<br> <br> # Сохраняем с суффиксом _with_expected<br> output_file = filepath.replace(".csv", "_with_expected.csv")<br> result_df.to_csv(output_file, index=False, float_format="%.4f")<br> print(f"✓ Добавлены ожидаемые значения для {filename} → {output_file}")<br> <br> <br> def main():<br> """Основная функция генерации всех тестовых данных"""<br> print("=" * 60)<br> print("Генерация тестовых данных для SG INDEX v4.2")<br> print("=" * 60)<br> <br> # Создаем директорию<br> create_output_dir()<br> <br> # Генерируем все наборы данных<br> generate_sanity_cases()<br> generate_monotonicity_cases()<br> generate_random_samples(1000)<br> generate_historical_scenarios()<br> generate_edge_cases()<br> generate_performance_batch(10000)<br> <br> # Генерируем ожидаемые значения<br> generate_expected_values()<br> <br> print("\n" + "=" * 60)<br> print("✅ ВСЕ ТЕСТОВЫЕ ДАННЫЕ СОЗДАНЫ УСПЕШНО")<br> print("=" * 60)<br> <br> # Сводная статистика<br> print("\n📊 Сводная статистика:")<br> data_dir = Path("tests/data")<br> for file in data_dir.glob("*.csv"):<br> df = pd.read_csv(file)<br> print(f" {file.name:30} - {len(df):6} записей")<br> <br> <br> if __name__ == "__main__":<br> main()<br> ```<br> <br> ---<br> <br> ## 8. Скрипт для запуска всех тестов с данными<br> <br> ```python<br> # tests/run_all_data_tests.py<br> """<br> Запуск всех тестов на всех тестовых данных<br> """<br> import pandas as pd<br> import numpy as np<br> from pathlib import Path<br> from src.sg_index_v42_final import SGIndexV42, IndexInput<br> import time<br> <br> <br> def test_sanity_cases():<br> """Тестирование на sanity cases"""<br> print("\n🧪 Тестирование sanity cases...")<br> df = pd.read_csv("tests/data/sanity_cases.csv")<br> model = SGIndexV42()<br> <br> passed = 0<br> total = len(df)<br> <br> for _, row in df.iterrows():<br> inputs = IndexInput(<br> C=row['C'],<br> V=row['V'],<br> T_loyalty=row['T_loyalty'],<br> Z=row['Z'],<br> sigma=row['sigma']<br> )<br> <br> result = model.compute(inputs)<br> expected_min = row['expected_S_official_min']<br> expected_max = row['expected_S_official_max']<br> <br> if expected_min <= result.S_official <= expected_max:<br> passed += 1<br> else:<br> print(f" ❌ {row['case_name']}: получено {result.S_official:.1f}, "<br> f"ожидалось {expected_min}-{expected_max}")<br> <br> print(f" Результат: {passed}/{total} пройдено")<br> return passed == total<br> <br> <br> def test_monotonicity():<br> """Тестирование монотонности"""<br> print("\n📈 Тестирование монотонности...")<br> df = pd.read_csv("tests/data/monotonicity_cases.csv")<br> model = SGIndexV42()<br> <br> test_types = df['test_type'].unique()<br> all_passed = True<br> <br> for test_type in test_types:<br> test_data = df[df['test_type'] == test_type].sort_values('sequence_id')<br> scores = []<br> <br> for _, row in test_data.iterrows():<br> inputs = IndexInput(<br> C=row['C'],<br> V=row['V'],<br> T_loyalty=row['T_loyalty'],<br> Z=row['Z'],<br> sigma=row['sigma']<br> )<br> result = model.compute(inputs)<br> scores.append(result.S_official)<br> <br> # Проверка монотонности<br> if 'inverse' in test_type:<br> # Должно убывать<br> is_monotone = all(scores[i] >= scores[i+1] - 0.001 <br> for i in range(len(scores)-1))<br> else:<br> # Должно возрастать<br> is_monotone = all(scores[i] <= scores[i+1] + 0.001 <br> for i in range(len(scores)-1))<br> <br> status = "✅" if is_monotone else "❌"<br> print(f" {status} {test_type:25} - {is_monotone}")<br> all_passed &= is_monotone<br> <br> return all_passed<br> <br> <br> def test_random_samples():<br> """Тестирование на случайных данных"""<br> print("\n🎲 Тестирование на случайных данных...")<br> df = pd.read_csv("tests/data/random_samples.csv")<br> model = SGIndexV42()<br> <br> errors = []<br> <br> for idx, row in df.iterrows():<br> try:<br> inputs = IndexInput(<br> C=row['C'],<br> V=row['V'],<br> T_loyalty=row['T_loyalty'],<br> Z=row['Z'],<br> sigma=row['sigma']<br> )<br> <br> result = model.compute(inputs)<br> <br> # Проверка диапазона<br> if not (0 <= result.S_official <= 100):<br> errors.append(f"Строка {idx+1}: S_official={result.S_official}")<br> <br> # Проверка отсутствия NaN/Inf<br> if np.isnan(result.S_official) or np.isinf(result.S_official):<br> errors.append(f"Строка {idx+1}: NaN/Inf значение")<br> <br> except Exception as e:<br> errors.append(f"Строка {idx+1}: {e}")<br> <br> total = len(df)<br> passed = total - len(errors)<br> <br> print(f" Протестировано: {total} записей")<br> print(f" Успешно: {passed}")<br> print(f" Ошибок: {len(errors)}")<br> <br> if errors:<br> print(f" Первые 3 ошибки:")<br> for err in errors[:3]:<br> print(f" {err}")<br> <br> return len(errors) == 0<br> <br> <br> def test_edge_cases():<br> """Тестирование экстремальных случаев"""<br> print("\n⚠️ Тестирование edge cases...")<br> df = pd.read_csv("tests/data/edge_cases.csv")<br> model = SGIndexV42()<br> <br> errors = []<br> <br> for _, row in df.iterrows():<br> try:<br> inputs = IndexInput(<br> C=row['C'],<br> V=row['V'],<br> T_loyalty=row['T_loyalty'],<br> Z=row['Z'],<br> sigma=row['sigma']<br> )<br> <br> result = model.compute(inputs)<br> <br> # Основные проверки<br> if not (0 <= result.S_official <= 100):<br> errors.append(f"{row['description']}: S_official={result.S_official}")<br> <br> if np.isnan(result.S_official) or np.isinf(result.S_official):<br> errors.append(f"{row['description']}: NaN/Inf")<br> <br> # Специфичные проверки для некоторых случаев<br> if row['description'] == 'all_max' and result.S_official < 99:<br> errors.append(f"all_max: ожидалось ~100, получено {result.S_official}")<br> <br> if row['description'] == 'max_volatility' and result.S_official > 20:<br> errors.append(f"max_volatility: ожидалось <20, получено {result.S_official}")<br> <br> except Exception as e:<br> errors.append(f"{row['description']}: {e}")<br> <br> total = len(df)<br> passed = total - len(errors)<br> <br> print(f" Протестировано: {total} edge cases")<br> print(f" Успешно: {passed}")<br> print(f" Ошибок: {len(errors)}")<br> <br> return len(errors) == 0<br> <br> <br> def performance_test():<br> """Нагрузочное тестирование"""<br> print("\n⚡ Нагрузочное тестирование...")<br> df = pd.read_csv("tests/data/performance_batch.csv")<br> model = SGIndexV42()<br> <br> # Тест скорости<br> start_time = time.time()<br> <br> for _, row in df.iterrows():<br> inputs = IndexInput(<br> C=row['C'],<br> V=row['V'],<br> T_loyalty=row['T_loyalty'],<br> Z=row['Z'],<br> sigma=row['sigma']<br> )<br> model.compute(inputs)<br> <br> elapsed = time.time() - start_time<br> n = len(df)<br> <br> print(f" Выполнено {n} расчётов за {elapsed:.2f} секунд")<br> print(f" Скорость: {n/elapsed:.0f} расчётов/сек")<br> print(f" Время на расчёт: {elapsed/n*1000:.2f} мс")<br> <br> # Проверка требований к производительности<br> speed_ok = n/elapsed > 1000 # >1000 расчётов в секунду<br> memory_ok = True # В реальном тесте нужно проверять память<br> <br> return speed_ok and memory_ok<br> <br> <br> def main():<br> """Запуск всех тестов"""<br> print("=" * 60)<br> print("ПОЛНАЯ ПРОВЕРКА SG INDEX v4.2 НА ТЕСТОВЫХ ДАННЫХ")<br> print("=" * 60)<br> <br> results = []<br> <br> # Запуск всех тестов<br> results.append(("Sanity Cases", test_sanity_cases()))<br> results.append(("Monotonicity", test_monotonicity()))<br> results.append(("Random Samples", test_random_samples()))<br> results.append(("Edge Cases", test_edge_cases()))<br> results.append(("Performance", performance_test()))<br> <br> # Итоговый отчет<br> print("\n" + "=" * 60)<br> print("ИТОГОВЫЙ ОТЧЕТ")<br> print("=" * 60)<br> <br> all_passed = True<br> for test_name, passed in results:<br> status = "✅ ПРОЙДЕНО" if passed else "❌ ПРОВАЛЕНО"<br> print(f"{status} - {test_name}")<br> all_passed &= passed<br> <br> print("\n" + "=" * 60)<br> if all_passed:<br> print("✅ ВСЕ ТЕСТЫ ПРОЙДЕНЫ УСПЕШНО!")<br> else:<br> print("❌ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ")<br> print("=" * 60)<br> <br> return all_passed<br> <br> <br> if __name__ == "__main__":<br> success = main()<br> exit(0 if success else 1)<br> ```<br> <br> ---<br> <br> ## 📊 Сводная статистика тестовых данных<br> <br> | Набор данных | Количество записей | Назначение |<br> |--------------|-------------------|------------|<br> | Sanity Cases | 5 | Проверка основных сценариев из документации |<br> | Monotonicity Cases | 24 | Проверка монотонности по каждому параметру |<br> | Random Samples | 1,000 | Статистическая проверка на случайных данных |<br> | Historical Scenarios | 20 | Имитация реальных исторических событий |<br> | Edge Cases | 25 | Проверка экстремальных и пограничных значений |<br> | Performance Batch | 10,000 | Нагрузочное тестирование |<br> | **Всего** | **~11,074** | **Полное покрытие тестами** |<br> <br> ---<br> <br> ## 🚀 Использование<br> <br> 1. **Создание всех тестовых данных:**<br> ```bash<br> python tests/generate_all_test_data.py<br> ```<br> <br> 2. **Запуск всех тестов:**<br> ```bash<br> python tests/run_all_data_tests.py<br> ```<br> <br> 3. **Интеграция с CI/CD:**<br> ```yaml<br> # В .github/workflows/validate-model.yml добавить:<br> - name: Run comprehensive data tests<br> run: python tests/run_all_data_tests.py<br> ```<br> <br> ---<br> <br> Этот комплексный набор тестовых данных обеспечит:<br> <br> ✅ **Полное покрытие** всех возможных сценариев <br> ✅ **Воспроизводимость** тестов (фиксированные seed) <br> ✅ **Проверку корректности** математики модели <br> ✅ **Валидацию монотонности** <br> ✅ **Нагрузочное тестирование** производительности <br> ✅ **Историческую валидацию** на реалистичных сценариях <br> ✅ **Автоматическую генерацию** и обновление данных<br>