grg 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grg/__init__.py +111 -0
- grg/cli.py +473 -0
- grg/config/grg-config.json +90 -0
- grg/config.py +120 -0
- grg/early_warning.py +272 -0
- grg/grg-config.json +90 -0
- grg/intervention.py +426 -0
- grg/metrics.py +921 -0
- grg-0.2.2.dist-info/METADATA +758 -0
- grg-0.2.2.dist-info/RECORD +14 -0
- grg-0.2.2.dist-info/WHEEL +5 -0
- grg-0.2.2.dist-info/entry_points.txt +2 -0
- grg-0.2.2.dist-info/licenses/LICENSE +21 -0
- grg-0.2.2.dist-info/top_level.txt +1 -0
grg/config.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Centralized configuration loader for GRG (Geometric Reasoning Governor)."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
DEFAULT_CONFIG_PATH = Path("config/grg-config.json")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def resolve_config_path() -> Path:
|
|
12
|
+
"""Find the config file regardless of current working directory."""
|
|
13
|
+
env_path = os.getenv("GRG_CONFIG")
|
|
14
|
+
if env_path:
|
|
15
|
+
return Path(env_path)
|
|
16
|
+
|
|
17
|
+
# 1. Try relative to CWD
|
|
18
|
+
cwd_path = Path.cwd() / DEFAULT_CONFIG_PATH
|
|
19
|
+
if cwd_path.exists():
|
|
20
|
+
return cwd_path
|
|
21
|
+
|
|
22
|
+
# 2. Try relative to this file (grg/config.py -> ./grg-config.json in same dir)
|
|
23
|
+
module_path = Path(__file__).parent / "grg-config.json"
|
|
24
|
+
if module_path.exists():
|
|
25
|
+
return module_path
|
|
26
|
+
|
|
27
|
+
# 3. Try relative to this file's parent (grg/config.py ->
|
|
28
|
+
# ../config/grg-config.json for installed packages)
|
|
29
|
+
package_path = Path(__file__).parent / "config" / "grg-config.json"
|
|
30
|
+
if package_path.exists():
|
|
31
|
+
return package_path
|
|
32
|
+
|
|
33
|
+
# 4. Try package data location (for development mode from src/)
|
|
34
|
+
dev_path = Path(__file__).parent.parent / "config" / "grg-config.json"
|
|
35
|
+
if dev_path.exists():
|
|
36
|
+
return dev_path
|
|
37
|
+
|
|
38
|
+
return DEFAULT_CONFIG_PATH
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_config() -> dict[str, Any]:
|
|
42
|
+
"""Load the GRG configuration from file or return defaults."""
|
|
43
|
+
# Global Defaults
|
|
44
|
+
config = {
|
|
45
|
+
"global": {
|
|
46
|
+
"ranking_profile": "conservative",
|
|
47
|
+
"grg": {
|
|
48
|
+
"window_size": 20,
|
|
49
|
+
"ema_weight": 0.3,
|
|
50
|
+
"warmup_steps": 50,
|
|
51
|
+
"lr_slash_factor": 0.1,
|
|
52
|
+
"lr_restore_factor": 1.0,
|
|
53
|
+
"thermal_damping_min_temp": 0.01,
|
|
54
|
+
"e_stop_horizon": 5,
|
|
55
|
+
"collapse_v_alpha_threshold": -0.02,
|
|
56
|
+
"stability_v_alpha_threshold": 0.005,
|
|
57
|
+
"trajectory_window_size": 500,
|
|
58
|
+
"trajectory_window_step": 50,
|
|
59
|
+
"v_alpha_panic_threshold": -0.02,
|
|
60
|
+
"alpha_safety_floor": 0.10,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
"profiles": {},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
config_path = resolve_config_path()
|
|
67
|
+
|
|
68
|
+
if config_path.exists():
|
|
69
|
+
try:
|
|
70
|
+
with open(config_path) as f:
|
|
71
|
+
user_config = json.load(f)
|
|
72
|
+
if "global" in user_config or "profiles" in user_config:
|
|
73
|
+
# New hierarchical format
|
|
74
|
+
if "global" in user_config:
|
|
75
|
+
config["global"].update(user_config["global"])
|
|
76
|
+
if "profiles" in user_config:
|
|
77
|
+
config["profiles"].update(user_config["profiles"])
|
|
78
|
+
else:
|
|
79
|
+
# Legacy flat format - merge into global
|
|
80
|
+
config["global"].update(user_config)
|
|
81
|
+
except Exception:
|
|
82
|
+
# Silently fall back to defaults if config fails to load
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
return config
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# Global config instance
|
|
89
|
+
_config_data = load_config()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ConfigWrapper:
|
|
93
|
+
"""Helper to access config with legacy support and profile awareness."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, data: dict[str, Any]):
|
|
96
|
+
self._data = data
|
|
97
|
+
|
|
98
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
99
|
+
"""Legacy access to global settings."""
|
|
100
|
+
return self._data["global"].get(key, default)
|
|
101
|
+
|
|
102
|
+
def get_profile(self, profile_name: str) -> dict[str, Any]:
|
|
103
|
+
"""Get profile merged with global defaults."""
|
|
104
|
+
profile = self._data["global"].copy()
|
|
105
|
+
profile.update(self._data["profiles"].get(profile_name, {}))
|
|
106
|
+
return profile
|
|
107
|
+
|
|
108
|
+
def get_grg_config(self, profile_name: str | None = None) -> dict[str, Any]:
|
|
109
|
+
"""Get GRG settings merged: global.grg -> profile.grg."""
|
|
110
|
+
grg = dict(self._data["global"].get("grg", {}))
|
|
111
|
+
if profile_name:
|
|
112
|
+
profile_grg = self._data["profiles"].get(profile_name, {}).get("grg", {})
|
|
113
|
+
grg.update(profile_grg)
|
|
114
|
+
return grg
|
|
115
|
+
|
|
116
|
+
def __getitem__(self, key: str) -> Any:
|
|
117
|
+
return self._data["global"][key]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
get_config = ConfigWrapper(_config_data)
|
grg/early_warning.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""Real-time streaming early warning + intervention system.
|
|
2
|
+
|
|
3
|
+
Combines macro (AlphaMomentumTracker velocity/momentum) with micro
|
|
4
|
+
(rolling rho_pn_sq decoupling) into a single EWI score. Streams each
|
|
5
|
+
reading to a JSONL log and fires intervention callbacks on threshold
|
|
6
|
+
crossing — before the trend fully manifests.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from grg.early_warning import EarlyWarningSystem
|
|
10
|
+
|
|
11
|
+
def my_intervention(reading):
|
|
12
|
+
print(f"INTERVENTION: {reading['ewi']:.4f} at step {reading['step']}")
|
|
13
|
+
|
|
14
|
+
ews = EarlyWarningSystem(
|
|
15
|
+
intervention_fn=my_intervention,
|
|
16
|
+
log_path="reports/ewi_stream.jsonl",
|
|
17
|
+
)
|
|
18
|
+
for value in live_feed:
|
|
19
|
+
ews.push(value)
|
|
20
|
+
"""
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import threading
|
|
24
|
+
import time
|
|
25
|
+
from collections import deque
|
|
26
|
+
from dataclasses import dataclass, asdict
|
|
27
|
+
from typing import Callable, Optional, List
|
|
28
|
+
|
|
29
|
+
import numpy as np
|
|
30
|
+
|
|
31
|
+
from grg.metrics import AlphaMomentumTracker, calculate_moment_sgo_metrics
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class EWIReading:
|
|
36
|
+
step: int
|
|
37
|
+
timestamp: float
|
|
38
|
+
alpha: float
|
|
39
|
+
v_alpha: float
|
|
40
|
+
m_alpha: float
|
|
41
|
+
rho_pn_sq: float
|
|
42
|
+
ewi: float
|
|
43
|
+
warning: bool
|
|
44
|
+
collapse: bool
|
|
45
|
+
intervention_fired: bool
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class EarlyWarningSystem:
|
|
49
|
+
"""Streaming early warning + intervention system.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
window_size : int
|
|
54
|
+
AlphaMomentumTracker window size.
|
|
55
|
+
alpha_ema : float
|
|
56
|
+
EMA smoothing for alpha.
|
|
57
|
+
k : int
|
|
58
|
+
Chunk size for rolling rho_pn_sq.
|
|
59
|
+
lambda_ : float
|
|
60
|
+
Weight for fragility (rho_pn_sq).
|
|
61
|
+
mu : float
|
|
62
|
+
Weight for negative velocity.
|
|
63
|
+
nu : float
|
|
64
|
+
Weight for negative momentum.
|
|
65
|
+
threshold : float
|
|
66
|
+
EWI threshold for intervention.
|
|
67
|
+
intervention_fn : callable, optional
|
|
68
|
+
Called with EWIReading when EWI crosses threshold.
|
|
69
|
+
log_path : str, optional
|
|
70
|
+
Path to JSONL log file for streaming output.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
window_size: int = 20,
|
|
76
|
+
alpha_ema: float = 0.3,
|
|
77
|
+
k: int = 3,
|
|
78
|
+
lambda_: float = 2.0,
|
|
79
|
+
mu: float = 3.0,
|
|
80
|
+
nu: float = 0.5,
|
|
81
|
+
safety_floor: float = 0.10,
|
|
82
|
+
panic_threshold: float = -0.02,
|
|
83
|
+
e_stop_horizon: float = 3.0,
|
|
84
|
+
threshold: float = 8.0,
|
|
85
|
+
intervention_fn: Optional[Callable] = None,
|
|
86
|
+
max_deltas: int = 200,
|
|
87
|
+
thread_safe: bool = True,
|
|
88
|
+
fsync_log: bool = False,
|
|
89
|
+
log_path: Optional[str] = None,
|
|
90
|
+
):
|
|
91
|
+
self.tracker = AlphaMomentumTracker(window_size=window_size, alpha_ema=alpha_ema)
|
|
92
|
+
self.k = k
|
|
93
|
+
self.lambda_ = lambda_
|
|
94
|
+
self.mu = mu
|
|
95
|
+
self.nu = nu
|
|
96
|
+
self.safety_floor = safety_floor
|
|
97
|
+
self.panic_threshold = panic_threshold
|
|
98
|
+
self.e_stop_horizon = e_stop_horizon
|
|
99
|
+
self.threshold = threshold
|
|
100
|
+
self.intervention_fn = intervention_fn
|
|
101
|
+
self.log_path = log_path
|
|
102
|
+
|
|
103
|
+
self._deltas: deque = deque(maxlen=max_deltas)
|
|
104
|
+
self._prev_alpha: Optional[float] = None
|
|
105
|
+
self._step: int = 0
|
|
106
|
+
self._last_warning: bool = False
|
|
107
|
+
self._readings: List[EWIReading] = []
|
|
108
|
+
self._lock = threading.Lock() if thread_safe else None
|
|
109
|
+
self._fsync_log = fsync_log
|
|
110
|
+
|
|
111
|
+
if log_path:
|
|
112
|
+
os.makedirs(os.path.dirname(log_path) or ".", exist_ok=True)
|
|
113
|
+
# Truncate on start
|
|
114
|
+
with open(log_path, "w"):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
def _rolling_rho(self) -> float:
|
|
118
|
+
"""Compute rho_pn_sq over the last k deltas."""
|
|
119
|
+
deltas = list(self._deltas)
|
|
120
|
+
if len(deltas) < self.k:
|
|
121
|
+
return 0.0
|
|
122
|
+
n_chunks = len(deltas) // self.k
|
|
123
|
+
if n_chunks < 1:
|
|
124
|
+
return 0.0
|
|
125
|
+
trimmed = np.array(deltas[: n_chunks * self.k]).reshape(n_chunks, self.k)
|
|
126
|
+
units = [row.tolist() for row in trimmed]
|
|
127
|
+
try:
|
|
128
|
+
m = calculate_moment_sgo_metrics(units, utility_power=1.1)
|
|
129
|
+
return float(m["rho_pn_sq"])
|
|
130
|
+
except Exception:
|
|
131
|
+
return 0.0
|
|
132
|
+
|
|
133
|
+
def push(self, value: float) -> EWIReading:
|
|
134
|
+
if self._lock is not None:
|
|
135
|
+
with self._lock:
|
|
136
|
+
return self._push(value)
|
|
137
|
+
return self._push(value)
|
|
138
|
+
|
|
139
|
+
def _push(self, value: float) -> EWIReading:
|
|
140
|
+
"""Push a new value through the system. Returns the EWI reading."""
|
|
141
|
+
self.tracker.push(value)
|
|
142
|
+
s = self.tracker.get_stats(v_panic=self.panic_threshold, floor=self.safety_floor)
|
|
143
|
+
|
|
144
|
+
# Track deltas for rolling rho
|
|
145
|
+
if self._prev_alpha is not None:
|
|
146
|
+
self._deltas.append(value - self._prev_alpha)
|
|
147
|
+
self._prev_alpha = value
|
|
148
|
+
|
|
149
|
+
rho = self._rolling_rho()
|
|
150
|
+
|
|
151
|
+
# EWI components
|
|
152
|
+
vel_warning = self.mu * max(0.0, -s["v_alpha"])
|
|
153
|
+
mom_warning = self.nu * max(0.0, -s["m_alpha"])
|
|
154
|
+
fragility = self.lambda_ * rho
|
|
155
|
+
ewi = vel_warning + mom_warning + fragility
|
|
156
|
+
|
|
157
|
+
collapse = self.tracker.should_stop(
|
|
158
|
+
safety_floor=self.safety_floor, panic_threshold=self.panic_threshold, e_stop_horizon=self.e_stop_horizon
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
warning = ewi >= self.threshold
|
|
162
|
+
intervention_fired = False
|
|
163
|
+
|
|
164
|
+
reading = EWIReading(
|
|
165
|
+
step=self._step,
|
|
166
|
+
timestamp=time.time(),
|
|
167
|
+
alpha=s["alpha"],
|
|
168
|
+
v_alpha=s["v_alpha"],
|
|
169
|
+
m_alpha=s["m_alpha"],
|
|
170
|
+
rho_pn_sq=rho,
|
|
171
|
+
ewi=ewi,
|
|
172
|
+
warning=warning,
|
|
173
|
+
collapse=collapse,
|
|
174
|
+
intervention_fired=False,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Fire intervention on threshold crossing (rising edge)
|
|
178
|
+
if warning and not self._last_warning:
|
|
179
|
+
reading.intervention_fired = True
|
|
180
|
+
if self.intervention_fn is not None:
|
|
181
|
+
try:
|
|
182
|
+
self.intervention_fn(reading)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"[EarlyWarning] Intervention callback error: {e}")
|
|
185
|
+
|
|
186
|
+
self._last_warning = warning
|
|
187
|
+
self._step += 1
|
|
188
|
+
|
|
189
|
+
# Stream to log (push, not batch)
|
|
190
|
+
if self.log_path:
|
|
191
|
+
with open(self.log_path, "a") as f:
|
|
192
|
+
f.write(json.dumps(asdict(reading)) + "\n")
|
|
193
|
+
f.flush()
|
|
194
|
+
if self._fsync_log:
|
|
195
|
+
os.fsync(f.fileno())
|
|
196
|
+
|
|
197
|
+
self._readings.append(reading)
|
|
198
|
+
return reading
|
|
199
|
+
|
|
200
|
+
def reset(self) -> None:
|
|
201
|
+
if self._lock is not None:
|
|
202
|
+
with self._lock:
|
|
203
|
+
self._reset_unlocked()
|
|
204
|
+
return
|
|
205
|
+
self._reset_unlocked()
|
|
206
|
+
|
|
207
|
+
def _reset_unlocked(self) -> None:
|
|
208
|
+
self.tracker = AlphaMomentumTracker(window_size=self.tracker.window_size, alpha_ema=self.tracker.alpha_ema)
|
|
209
|
+
self._deltas.clear()
|
|
210
|
+
self._prev_alpha = None
|
|
211
|
+
self._step = 0
|
|
212
|
+
self._last_warning = False
|
|
213
|
+
self._readings.clear()
|
|
214
|
+
|
|
215
|
+
def push_many(self, values) -> list:
|
|
216
|
+
return [self.push(float(v)) for v in values]
|
|
217
|
+
|
|
218
|
+
def get_history(self) -> List[EWIReading]:
|
|
219
|
+
return list(self._readings)
|
|
220
|
+
|
|
221
|
+
def summary(self) -> dict:
|
|
222
|
+
warnings = [r for r in self._readings if r.warning]
|
|
223
|
+
interventions = [r for r in self._readings if r.intervention_fired]
|
|
224
|
+
return {
|
|
225
|
+
"total_steps": self._step,
|
|
226
|
+
"warning_steps": len(warnings),
|
|
227
|
+
"intervention_count": len(interventions),
|
|
228
|
+
"first_warning_step": warnings[0].step if warnings else None,
|
|
229
|
+
"max_ewi": max((r.ewi for r in self._readings), default=0.0),
|
|
230
|
+
"final_alpha": self._readings[-1].alpha if self._readings else None,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ------------------------------------------------------------------
|
|
235
|
+
# Demo: stream exponential series through the system
|
|
236
|
+
# ------------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
def demo():
|
|
239
|
+
import numpy as np
|
|
240
|
+
|
|
241
|
+
def intervention_handler(reading: EWIReading):
|
|
242
|
+
print(f" >>> INTERVENTION FIRED at step {reading.step}: "
|
|
243
|
+
f"EWI={reading.ewi:.4f} (alpha={reading.alpha:.4f}, "
|
|
244
|
+
f"v={reading.v_alpha:.4f}, rho={reading.rho_pn_sq:.4f})")
|
|
245
|
+
|
|
246
|
+
series = {
|
|
247
|
+
"decay": [float(np.exp(-0.03 * t)) for t in range(50)],
|
|
248
|
+
"growth": [float(1.0 - np.exp(-0.03 * t)) for t in range(50)],
|
|
249
|
+
"zigzag": [float(0.5 * np.sin(0.3 * t)) for t in range(50)],
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for name, vals in series.items():
|
|
253
|
+
print(f"\n{'=' * 60}")
|
|
254
|
+
print(f" Streaming: {name}")
|
|
255
|
+
print(f"{'=' * 60}")
|
|
256
|
+
ews = EarlyWarningSystem(
|
|
257
|
+
intervention_fn=intervention_handler,
|
|
258
|
+
log_path=f"reports/ewi_{name}.jsonl",
|
|
259
|
+
threshold=0.05,
|
|
260
|
+
)
|
|
261
|
+
for v in vals:
|
|
262
|
+
ews.push(v)
|
|
263
|
+
sm = ews.summary()
|
|
264
|
+
print(f" Summary: {sm['total_steps']} steps, "
|
|
265
|
+
f"{sm['warning_steps']} warnings, "
|
|
266
|
+
f"{sm['intervention_count']} interventions, "
|
|
267
|
+
f"first warning at step {sm['first_warning_step']}, "
|
|
268
|
+
f"max EWI={sm['max_ewi']:.4f}")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
demo()
|
grg/grg-config.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"global": {
|
|
3
|
+
"ranking_profile": "conservative",
|
|
4
|
+
"grg": {
|
|
5
|
+
"window_size": 20,
|
|
6
|
+
"ema_weight": 0.3,
|
|
7
|
+
"warmup_steps": 50,
|
|
8
|
+
"lr_slash_factor": 0.1,
|
|
9
|
+
"lr_restore_factor": 1.0,
|
|
10
|
+
"thermal_damping_min_temp": 0.01,
|
|
11
|
+
"e_stop_horizon": 5,
|
|
12
|
+
"collapse_v_alpha_threshold": -0.02,
|
|
13
|
+
"stability_v_alpha_threshold": 0.005,
|
|
14
|
+
"trajectory_window_size": 500,
|
|
15
|
+
"trajectory_window_step": 50,
|
|
16
|
+
"v_alpha_panic_threshold": -0.02,
|
|
17
|
+
"alpha_safety_floor": 0.10
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"profiles": {
|
|
21
|
+
"default": {
|
|
22
|
+
"grg": {
|
|
23
|
+
"window_size": 20,
|
|
24
|
+
"ema_weight": 0.3,
|
|
25
|
+
"warmup_steps": 50,
|
|
26
|
+
"lr_slash_factor": 0.1,
|
|
27
|
+
"lr_restore_factor": 1.0,
|
|
28
|
+
"thermal_damping_min_temp": 0.01,
|
|
29
|
+
"e_stop_horizon": 5,
|
|
30
|
+
"collapse_v_alpha_threshold": -0.02,
|
|
31
|
+
"stability_v_alpha_threshold": 0.005,
|
|
32
|
+
"trajectory_window_size": 500,
|
|
33
|
+
"trajectory_window_step": 50,
|
|
34
|
+
"v_alpha_panic_threshold": -0.02,
|
|
35
|
+
"alpha_safety_floor": 0.10
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"high_sensitivity": {
|
|
39
|
+
"grg": {
|
|
40
|
+
"window_size": 16,
|
|
41
|
+
"ema_weight": 0.4,
|
|
42
|
+
"warmup_steps": 30,
|
|
43
|
+
"lr_slash_factor": 0.2,
|
|
44
|
+
"lr_restore_factor": 1.0,
|
|
45
|
+
"thermal_damping_min_temp": 0.02,
|
|
46
|
+
"e_stop_horizon": 3,
|
|
47
|
+
"collapse_v_alpha_threshold": -0.015,
|
|
48
|
+
"stability_v_alpha_threshold": 0.003,
|
|
49
|
+
"trajectory_window_size": 300,
|
|
50
|
+
"trajectory_window_step": 30,
|
|
51
|
+
"v_alpha_panic_threshold": -0.01,
|
|
52
|
+
"alpha_safety_floor": 0.15
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"low_latency": {
|
|
56
|
+
"grg": {
|
|
57
|
+
"window_size": 12,
|
|
58
|
+
"ema_weight": 0.5,
|
|
59
|
+
"warmup_steps": 20,
|
|
60
|
+
"lr_slash_factor": 0.5,
|
|
61
|
+
"lr_restore_factor": 1.0,
|
|
62
|
+
"thermal_damping_min_temp": 0.01,
|
|
63
|
+
"e_stop_horizon": 2,
|
|
64
|
+
"collapse_v_alpha_threshold": -0.025,
|
|
65
|
+
"stability_v_alpha_threshold": 0.008,
|
|
66
|
+
"trajectory_window_size": 200,
|
|
67
|
+
"trajectory_window_step": 20,
|
|
68
|
+
"v_alpha_panic_threshold": -0.03,
|
|
69
|
+
"alpha_safety_floor": 0.08
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"rag_grounded": {
|
|
73
|
+
"grg": {
|
|
74
|
+
"window_size": 24,
|
|
75
|
+
"ema_weight": 0.25,
|
|
76
|
+
"warmup_steps": 100,
|
|
77
|
+
"lr_slash_factor": 0.1,
|
|
78
|
+
"lr_restore_factor": 1.0,
|
|
79
|
+
"thermal_damping_min_temp": 0.005,
|
|
80
|
+
"e_stop_horizon": 8,
|
|
81
|
+
"collapse_v_alpha_threshold": -0.01,
|
|
82
|
+
"stability_v_alpha_threshold": 0.002,
|
|
83
|
+
"trajectory_window_size": 1000,
|
|
84
|
+
"trajectory_window_step": 100,
|
|
85
|
+
"v_alpha_panic_threshold": -0.005,
|
|
86
|
+
"alpha_safety_floor": 0.20
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|