File size: 8,070 Bytes
837b5a1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/env python3
"""
EvoRM Configuration — Paper Section IV-A
=========================================
Centralized hyperparameter configuration for the EvoRM framework.
All values follow the paper's experimental setup (Section IV-A).

Usage:
    from evorm_config import EvoRMConfig
    config = EvoRMConfig()
    plugin = EvoRMPlugin(client=client, config=config)
"""

from dataclasses import dataclass, field, asdict
from typing import Dict, Optional
import json
import os


@dataclass
class EvoRMConfig:
    """Centralized configuration for all EvoRM components."""
    
    # =========================================================================
    # Hypergraph Storage (Section III-D)
    # =========================================================================
    alpha: float = 0.6              # Weight for symbolic confidence in wk
    beta: float = 0.4               # Weight for neural similarity in wk
    merge_threshold: float = 0.5    # eta_h: Jaccard threshold for hyperedge merging
    K: int = 5                      # Top-K hyperedges for evidence subgraph
    
    # =========================================================================
    # Two-Stage Inference (Section III-C)
    # =========================================================================
    theta_hi: float = 0.7           # High confidence threshold for direct match
    theta_prune: float = 0.5        # Threshold for direct non-match
    theta_gate: float = 0.5         # MLP gate threshold (Section III-E)
    
    # =========================================================================
    # MLP Gate (Section III-E)
    # =========================================================================
    mlp_input_dim: int = 64         # Input feature dimension
    mlp_hidden_dims: list = field(default_factory=lambda: [256, 128, 64])
    mlp_n_warmup: int = 500         # N_warmup: warmup trajectories before training
    mlp_epochs: int = 50            # Training epochs for self-supervised MLP
    mlp_batch_size: int = 32        # Batch size for MLP training
    mlp_learning_rate: float = 0.001
    
    # =========================================================================
    # Rule Maintenance (Section III-F)
    # =========================================================================
    decay_lambda: float = 0.01      # Freshness decay rate
    freshness_threshold: float = 0.1  # theta_f: below this → stale
    confidence_threshold: float = 0.2  # theta_c: below this → flip candidate
    eval_triggers: int = 10         # N_eval: triggers before evaluation
    merge_similarity: float = 0.7   # eta_m: Jaccard for rule merging
    max_rules: int = 10000          # Maximum rules before eviction
    evict_percentile: float = 0.1   # Bottom percentile to evict
    optimization_interval: int = 300  # Seconds between maintenance cycles
    
    # =========================================================================
    # Entity Embedding (Section III-D)
    # =========================================================================
    demb: int = 1024                # Entity embedding dimension
    n_features: int = 8192          # Hash features for embedding
    
    # =========================================================================
    # LLM API (Section IV-A)
    # =========================================================================
    model: str = "gpt-3.5-turbo-1106"
    mlight_temperature: float = 0.1
    mheavy_temperature: float = 0.0
    api_timeout: int = 30           # API timeout in seconds
    api_max_retries: int = 3        # Max retries with exponential backoff
    
    # =========================================================================
    # Experiment Control
    # =========================================================================
    max_workers: int = 5            # ThreadPoolExecutor max_workers
    ablation_mode: Optional[str] = None
    # None = full system
    # 'no_stage1' = skip Stage 1 symbolic filtering
    # 'no_maintenance' = skip rule maintenance
    # 'no_mlp_gate' = disable MLP gate
    # 'no_mlight' = use legacy single-call instead of Mlight+Mheavy
    # 'no_hypergraph' = flat layout without hypergraph structure
    
    # =========================================================================
    # Persistence
    # =========================================================================
    persistence_dir: Optional[str] = None
    
    def to_dict(self) -> Dict:
        """Convert to dictionary for serialization."""
        d = asdict(self)
        # Convert list fields
        d['mlp_hidden_dims'] = list(self.mlp_hidden_dims)
        return d
    
    @classmethod
    def from_dict(cls, d: Dict) -> 'EvoRMConfig':
        """Create config from dictionary."""
        return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
    
    def save(self, path: str):
        """Save config to JSON file."""
        os.makedirs(os.path.dirname(path) or '.', exist_ok=True)
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
        print(f"Config saved to {path}")
    
    @classmethod
    def load(cls, path: str) -> 'EvoRMConfig':
        """Load config from JSON file."""
        with open(path, 'r', encoding='utf-8') as f:
            d = json.load(f)
        return cls.from_dict(d)
    
    def __repr__(self) -> str:
        return (f"EvoRMConfig(alpha={self.alpha}, beta={self.beta}, "
                f"theta_hi={self.theta_hi}, theta_prune={self.theta_prune}, "
                f"model={self.model}, ablation={self.ablation_mode})")


# ==============================================================================
# Preset configurations for experiments
# ==============================================================================

def get_default_config() -> EvoRMConfig:
    """Default configuration matching paper's experimental setup."""
    return EvoRMConfig()


def get_ablation_configs() -> Dict[str, EvoRMConfig]:
    """Get all ablation configurations for Table VI experiments."""
    configs = {}
    
    # Full system
    configs['full'] = EvoRMConfig()
    
    # w/o Stage 1 (symbolic filtering)
    configs['no_stage1'] = EvoRMConfig(ablation_mode='no_stage1')
    
    # w/o Maintenance
    configs['no_maintenance'] = EvoRMConfig(ablation_mode='no_maintenance')
    
    # w/o MLP Gate
    configs['no_mlp_gate'] = EvoRMConfig(ablation_mode='no_mlp_gate')
    
    # w/o Hypergraph (flat layout)
    configs['no_hypergraph'] = EvoRMConfig(ablation_mode='no_hypergraph')
    
    # w/o Mlight (single-call)
    configs['no_mlight'] = EvoRMConfig(ablation_mode='no_mlight')
    
    return configs


def get_efficiency_configs() -> Dict[str, EvoRMConfig]:
    """Get configurations for efficiency experiments (varying theta_hi)."""
    configs = {}
    for theta in [0.5, 0.6, 0.7, 0.8, 0.9]:
        configs[f'theta_hi_{theta}'] = EvoRMConfig(theta_hi=theta)
    return configs


# ==============================================================================
# Test / Demo
# ==============================================================================

if __name__ == "__main__":
    print("EvoRM Configuration - Self Test")
    print("=" * 60)
    
    # Default config
    config = get_default_config()
    print(f"\n1. Default config: {config}")
    
    # Save and load
    config.save('/tmp/evorm_test_config.json')
    loaded = EvoRMConfig.load('/tmp/evorm_test_config.json')
    print(f"2. Loaded config: {loaded}")
    print(f"   Match: {config.to_dict() == loaded.to_dict()}")
    
    # Ablation configs
    print(f"\n3. Ablation configs:")
    for name, cfg in get_ablation_configs().items():
        print(f"   {name}: ablation={cfg.ablation_mode}")
    
    # Efficiency configs
    print(f"\n4. Efficiency configs:")
    for name, cfg in get_efficiency_configs().items():
        print(f"   {name}: theta_hi={cfg.theta_hi}")
    
    print("\n✅ All config tests passed!")