Skip to content

Commit 9b2fb40

Browse files
committed
2026-03-04 Model Registry architecture, eliminate 5-place coupling
1 parent 070ddb8 commit 9b2fb40

File tree

9 files changed

+515
-433
lines changed

9 files changed

+515
-433
lines changed

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,37 @@ All notable changes to Vectrix will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.0.13] - 2026-03-04
9+
10+
Model Registry architecture — eliminates coupling between model metadata, model creation, and orchestration. Adding a new model now requires editing only 1 file instead of 5.
11+
12+
### Changed
13+
14+
**Architecture: Model Registry Pattern (`engine/registry.py`)**
15+
- Created `engine/registry.py` as the Single Source of Truth for all model metadata
16+
- `ModelSpec` dataclass: modelId, name, description, factory, needsPeriod, minData, flatResistance, bestFor, refitStrategy
17+
- `createModel(modelId, period)`: unified model instantiation — replaces 200-line if/elif chain in vectrix.py
18+
- `getRegistry()`, `getModelSpec()`, `listModelIds()`, `getModelInfo()`: centralized access to model information
19+
- Adding a new forecasting model now requires only 1 edit: add a `ModelSpec` entry to `registry.py`
20+
21+
**vectrix.py Refactored (959 → 709 lines, -26%)**
22+
- Removed `NATIVE_MODELS` class dict (80+ lines) — replaced by `getRegistry()`
23+
- Removed `_fitAndPredictNativeWithCache` 200-line if/elif chain — replaced by `createModel()` + `fit()` + `predict()`
24+
- Refit strategies now driven by `ModelSpec.refitStrategy` field instead of model-specific if/elif
25+
- `_getModelName()` static method replaces repeated dict lookups
26+
27+
**types.py MODEL_INFO Unified**
28+
- `MODEL_INFO` is now a lazy-loading proxy that reads from `engine/registry.py` on first access
29+
- Backward compatible: all existing code using `MODEL_INFO[modelId]` continues to work
30+
- Eliminates duplicate model metadata between types.py and vectrix.py
31+
32+
### Added
33+
34+
- `engine/registry.py`: Model registry module with `ModelSpec`, `getRegistry()`, `createModel()`, `getModelInfo()`
35+
- `CLAUDE.md`: Architecture design philosophy section documenting registry pattern, module roles, dependency direction, and engine interface contract
36+
37+
[0.0.13]: https://github.com/eddmpython/vectrix/compare/v0.0.12...v0.0.13
38+
839
## [0.0.12] - 2026-03-04
940

1041
DOT-Hybrid holdout validation release — 8-way config selection for period>1 data now uses holdout validation instead of in-sample MAE, reducing overfitting on Quarterly (-1.25%) and Monthly (-2.55%) forecasts. AVG OWA improved from 0.8831 to ~0.876.

CLAUDE.md

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -355,31 +355,90 @@ src/vectrix/
355355
- 구현: numpy + scipy.optimize, 난이도 중
356356
- 기대: "가장 놀랍지 않은" 예측 = 과적합 방지
357357

358-
## 확장/유지보수 원칙 (2026-03-04 확정)
358+
## 아키텍처 설계 사상 (2026-03-04 확정)
359359

360-
### API 설계 원칙
360+
### [최우선] 결합도 최소화 — "1곳 수정 = 1곳만 수정"
361+
362+
**과거 문제**: 모델 추가 시 5곳 수정 필요 (NATIVE_MODELS, _fitAndPredictNativeWithCache, _refitModelOnFullData, _selectNativeModels, MODEL_INFO) → 스파게티 코드, 연쇄 버그
363+
364+
**해결: 레지스트리 패턴 (engine/registry.py)**
365+
366+
```
367+
engine/registry.py ← 모든 모델 메타데이터의 Single Source of Truth
368+
369+
├── ModelSpec: modelId, name, description, factory, needsPeriod, minData, flatResistance, bestFor
370+
├── getRegistry() → Dict[str, ModelSpec]
371+
├── createModel(modelId, period) → model instance
372+
└── getModelInfo() → backward-compatible MODEL_INFO dict
373+
```
374+
375+
**새 모델 추가 절차 (1곳만 수정)**:
376+
1. `engine/newmodel.py` 작성 (fit/predict 인터페이스)
377+
2. `engine/registry.py`에 ModelSpec 1개 추가
378+
3. 끝. vectrix.py, types.py, easy.py, docs 자동 반영
379+
380+
**절대 금지**:
381+
- vectrix.py에 모델별 if/elif 분기 추가
382+
- types.py에 모델 메타데이터 중복
383+
- easy.py에 모델 로직 중복 구현
384+
385+
### 모듈 역할 분리 — 단방향 의존성
386+
387+
```
388+
easy.py → vectrix.py → engine/registry.py → engine/*.py
389+
│ │
390+
└── types.py ←────────────┘
391+
```
392+
393+
- **easy.py**: 사용자 인터페이스 전용. 데이터 파싱 + Vectrix 래핑. 자체 로직 없음
394+
- **vectrix.py**: 오케스트레이터. 분석/모델선택/학습/앙상블 조율. 모델 생성은 registry 위임
395+
- **engine/registry.py**: 모델 메타데이터 중앙 저장소. 모든 모델 정보의 유일한 출처
396+
- **engine/*.py**: 순수 예측 엔진. 외부 의존성 없음. fit(y) → predict(steps) 계약만 준수
397+
- **types.py**: 데이터 타입 정의만. 모델 메타데이터 보관 금지 (registry.py로 이전)
398+
399+
### 엔진 인터페이스 계약
400+
401+
모든 예측 모델은 이 계약을 반드시 준수:
402+
403+
```python
404+
class ForecastModel:
405+
def fit(self, y: np.ndarray) -> self:
406+
"""학습. y는 1D float64 배열."""
407+
408+
def predict(self, steps: int) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
409+
"""예측. (predictions, lower95, upper95) 반환."""
410+
```
411+
412+
- `fit()``self` 반환 (체이닝 가능)
413+
- `predict()`는 항상 3-tuple 반환
414+
- 생성자에서 `period` 파라미터는 선택적 (registry.ModelSpec.needsPeriod로 관리)
415+
- 학습 전 predict() 호출 시 ValueError raise
416+
417+
### 확장/유지보수 원칙
418+
419+
#### API 설계 원칙
361420
1. **Progressive Disclosure**: Level 1(제로설정) → Level 2(가이드 제어) → Level 3(엔진 직접)
362421
2. **새 파라미터 추가 시 반드시 기본값 제공**`forecast(data, steps=12)`는 영원히 동작해야 함
363422
3. **Easy API 파라미터는 Vectrix 클래스의 기능을 투과** — easy.py가 vectrix.py를 래핑, 중복 구현 금지
364423
4. **파라미터 네이밍**: Easy API는 snake_case 허용 (models, ensemble, confidence), 내부는 camelCase
365424

366-
### 엔진 확장 원칙
367-
1. **새 모델 추가 시**: engine/ 아래 독립 파일, `fit(data)` + `predict(steps)` 인터페이스 준수
425+
#### 엔진 확장 원칙
426+
1. **새 모델 = registry.py에 ModelSpec 1개 추가** — 다른 파일 수정 불필요
368427
2. **M4 100K 벤치마크 통과 필수** — OWA < 1.0 (Naive2 대비) 확인 후 통합
369-
3. **NATIVE_MODELS dict에 등록** + `__init__.py` export + 테스트 추가
370-
4. **잔차 다양성 우선** — 기존 모델과 잔차 상관 < 0.5인 모델이 앙상블에 가치 있음
428+
3. **잔차 다양성 우선** — 기존 모델과 잔차 상관 < 0.5인 모델이 앙상블에 가치 있음
429+
4. **engine/__init__.py에서 export** + tests/ 테스트 추가
371430

372-
### 속도 확장 원칙
431+
#### 속도 확장 원칙
373432
1. **핫 루프 식별 → Rust 이전** — profiling으로 병목 확인 후 rust/src/lib.rs에 추가
374433
2. **Python 오버헤드 최소화** — 모델 선택/CV 로직의 불필요한 복사/변환 제거
375434
3. **벤치마크 측정 필수** — 변경 전후 `forecast()` 전체 latency 비교
376435

377-
### 정확도 확장 원칙
436+
#### 정확도 확장 원칙
378437
1. **앙상블 전략이 단일 모델보다 중요** — DNA 기반 가중치, 잔차 다양성 활용
379438
2. **빈도별 전략 분리** — Yearly/Quarterly는 Theta계열, Hourly/Daily는 다중 계절성 특화
380439
3. **실험 → 검증 → 통합 파이프라인** — experiments/에서 실험, M4로 검증, engine/으로 통합
381440

382-
### 문서/마케팅 원칙
441+
#### 문서/마케팅 원칙
383442
1. **모든 주장에 벤치마크 수치 첨부** — "빠르다"가 아닌 "5.6x faster (295ms → 52ms)"
384443
2. **블로그는 교육 중심** — 기초부터 설명, Vectrix 홍보는 자연스럽게 녹여냄
385444
3. **비교 표는 공정하게** — 경쟁사의 장점도 인정, 우리가 약한 부분도 투명하게 공개

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "vectrix"
3-
version = "0.0.10"
3+
version = "0.0.13"
44
edition = "2021"
55
license-file = "LICENSE"
66

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "vectrix"
3-
version = "0.0.12"
3+
version = "0.0.13"
44
description = "Zero-config time series forecasting & analysis library. 30+ models with built-in Rust engine for blazing-fast performance."
55
readme = "README.md"
66
license = {file = "LICENSE"}

src/vectrix/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
)
8484
from .vectrix import Vectrix
8585

86-
__version__ = "0.0.12"
86+
__version__ = "0.0.13"
8787
__all__ = [
8888
"Vectrix",
8989
"ForecastResult",

src/vectrix/engine/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from .tbats import TBATS, AutoTBATS
3434
from .theta import ThetaModel
3535
from .tsfeatures import TSFeatureExtractor
36+
from .registry import ModelSpec, createModel, getModelInfo, getModelSpec, getRegistry, listModelIds
3637
from .turbo import TurboCore
3738
from .var import VARModel, VECMModel
3839

@@ -86,4 +87,10 @@
8687
"AdaptiveThetaEnsemble",
8788
"EchoStateForecaster",
8889
"DynamicTimeScanForecaster",
90+
"ModelSpec",
91+
"getRegistry",
92+
"getModelSpec",
93+
"listModelIds",
94+
"createModel",
95+
"getModelInfo",
8996
]

0 commit comments

Comments
 (0)