-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcalibrate_composition_rules.py
More file actions
602 lines (521 loc) · 20.7 KB
/
calibrate_composition_rules.py
File metadata and controls
602 lines (521 loc) · 20.7 KB
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
#!/usr/bin/env python3
"""
SPDX-License-Identifier: MIT
Composition Rule Calibration
This script learns optimal coupling constants and structural bonuses
from empirical LJPW data collected from the real Python Code Harmonizer.
The goal is to minimize prediction error by adjusting:
1. Coupling constants (κ_LJ, κ_LP, κ_JL, κ_WL)
2. Structural feature bonuses
3. Harmony effects
Based on empirical findings:
- Simple compositions: 10-15% error (good!)
- Complex compositions: 30-98% error (needs calibration)
- Average: ~30% overestimation
Strategy:
- Use gradient descent or scipy.optimize to find optimal constants
- Validate on held-out test cases
- Generate updated composition rules
"""
import math
import sys
from dataclasses import dataclass
from typing import Dict, List, Tuple
import os
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)
from harmonizer_integration import PythonCodeHarmonizer, HARMONIZER_AVAILABLE
@dataclass
class LJPWProfile:
"""4D LJPW semantic profile."""
L: float
J: float
P: float
W: float
def distance_to(self, other: "LJPWProfile") -> float:
"""Euclidean distance in 4D space."""
return math.sqrt(
(self.L - other.L) ** 2
+ (self.J - other.J) ** 2
+ (self.P - other.P) ** 2
+ (self.W - other.W) ** 2
)
def __repr__(self):
return f"LJPW(L={self.L:.3f}, J={self.J:.3f}, P={self.P:.3f}, W={self.W:.3f})"
@dataclass
class CompositionExample:
"""A training example: components + structure → actual profile."""
components: List[LJPWProfile]
structural_features: Dict[str, bool]
actual_profile: LJPWProfile
description: str
@dataclass
class CouplingConstants:
"""The coupling constants we want to learn."""
# Current values (from theory)
κ_LJ: float = 1.2 # Love amplifies Justice
κ_LP: float = 1.3 # Love amplifies Power
κ_JL: float = 1.2 # Justice supports Love
κ_WL: float = 1.1 # Wisdom amplifies Love
# Structural bonuses (per feature)
bonus_docstring: float = 0.10
bonus_type_hints: float = 0.05
bonus_error_handling: float = 0.08
bonus_logging: float = 0.12
bonus_testing: float = 0.15
bonus_state: float = 0.15
bonus_history: float = 0.20
bonus_validation: float = 0.10
def to_vector(self) -> List[float]:
"""Convert to optimization vector."""
return [
self.κ_LJ,
self.κ_LP,
self.κ_JL,
self.κ_WL,
self.bonus_docstring,
self.bonus_type_hints,
self.bonus_error_handling,
self.bonus_logging,
self.bonus_testing,
self.bonus_state,
self.bonus_history,
self.bonus_validation,
]
@staticmethod
def from_vector(vec: List[float]) -> "CouplingConstants":
"""Create from optimization vector."""
return CouplingConstants(
κ_LJ=vec[0],
κ_LP=vec[1],
κ_JL=vec[2],
κ_WL=vec[3],
bonus_docstring=vec[4],
bonus_type_hints=vec[5],
bonus_error_handling=vec[6],
bonus_logging=vec[7],
bonus_testing=vec[8],
bonus_state=vec[9],
bonus_history=vec[10],
bonus_validation=vec[11],
)
class CompositionPredictor:
"""Predicts LJPW profiles using parameterized composition rules."""
def __init__(self, constants: CouplingConstants):
self.constants = constants
def predict(
self, components: List[LJPWProfile], features: Dict[str, bool]
) -> LJPWProfile:
"""
Predict composed LJPW profile.
Algorithm:
1. Aggregate component profiles (average)
2. Apply coupling amplification
3. Add structural bonuses
4. Apply harmony effects
"""
if not components:
return LJPWProfile(0, 0, 0, 0)
# Step 1: Aggregate (simple average)
n = len(components)
base_L = sum(c.L for c in components) / n
base_J = sum(c.J for c in components) / n
base_P = sum(c.P for c in components) / n
base_W = sum(c.W for c in components) / n
# Step 2: Coupling amplification
# Love amplifies Justice and Power
coupled_J = base_J * (1.0 + base_L * (self.constants.κ_LJ - 1.0))
coupled_P = base_P * (1.0 + base_L * (self.constants.κ_LP - 1.0))
# Justice supports Love
coupled_L = base_L * (1.0 + base_J * (self.constants.κ_JL - 1.0))
# Wisdom amplifies Love
coupled_L = coupled_L * (1.0 + base_W * (self.constants.κ_WL - 1.0))
L, J, P, W = coupled_L, coupled_J, coupled_P, base_W
# Step 3: Structural bonuses
if features.get("has_docstring", False):
L += self.constants.bonus_docstring
W += self.constants.bonus_docstring * 0.5
if features.get("has_type_hints", False):
W += self.constants.bonus_type_hints
if features.get("has_error_handling", False):
J += self.constants.bonus_error_handling
if features.get("has_logging", False):
L += self.constants.bonus_logging
if features.get("has_testing", False):
J += self.constants.bonus_testing
if features.get("has_state", False):
W += self.constants.bonus_state
J += self.constants.bonus_state * 0.5
if features.get("has_history", False):
L += self.constants.bonus_history
W += self.constants.bonus_history * 0.3
if features.get("has_validation", False):
J += self.constants.bonus_validation
# Step 4: Harmony effects (when multiple features present)
feature_count = sum(1 for v in features.values() if v)
if feature_count >= 3:
harmony_bonus = 0.05 * (feature_count - 2)
L += harmony_bonus
J += harmony_bonus
W += harmony_bonus
# Clamp to [0, 1]
L = max(0.0, min(1.0, L))
J = max(0.0, min(1.0, J))
P = max(0.0, min(1.0, P))
W = max(0.0, min(1.0, W))
return LJPWProfile(L, J, P, W)
def collect_training_data() -> List[CompositionExample]:
"""
Collect training examples from empirical validation.
These are the actual measurements from running experiments
with the real harmonizer.
"""
examples = []
# Example 1: secure_add composition (Level 1)
# Components: add_simple (L=1.0) + validate_numeric (J=1.0) + log_operation (L=0.5, J=0.5)
examples.append(
CompositionExample(
components=[
LJPWProfile(1.0, 0.0, 0.0, 0.0), # add_simple
LJPWProfile(0.0, 1.0, 0.0, 0.0), # validate_numeric
LJPWProfile(0.5, 0.5, 0.0, 0.0), # log_operation
],
structural_features={
"has_validation": True,
"has_logging": True,
},
actual_profile=LJPWProfile(0.2, 0.2, 0.2, 0.4),
description="secure_add function",
)
)
# Example 2: SimpleCalculator (Level 2)
# Components: simple_add (L=0.333, J=0.333, P=0.333) + simple_multiply (same)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.333, 0.333, 0.333, 0.0), # simple_add
LJPWProfile(0.333, 0.333, 0.333, 0.0), # simple_multiply
],
structural_features={},
actual_profile=LJPWProfile(0.333, 0.333, 0.333, 0.0),
description="SimpleCalculator class",
)
)
# Example 3: SecureCalculator (Level 2)
# Components: 4 secure functions (all L=0.25, J=0.25, P=0.25, W=0.25)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.25, 0.25, 0.25, 0.25), # secure_add
LJPWProfile(0.25, 0.25, 0.25, 0.25), # secure_subtract
LJPWProfile(0.25, 0.25, 0.25, 0.25), # secure_multiply
LJPWProfile(0.25, 0.25, 0.25, 0.25), # secure_divide
],
structural_features={},
actual_profile=LJPWProfile(0.25, 0.25, 0.25, 0.25),
description="SecureCalculator class",
)
)
# Add more examples from empirical validation...
# Example 4: secure_subtract (Level 1)
# Similar to secure_add but with subtract
examples.append(
CompositionExample(
components=[
LJPWProfile(0.0, 0.0, 0.0, 0.0), # subtract_simple
LJPWProfile(0.0, 1.0, 0.0, 0.0), # validate_numeric
LJPWProfile(0.5, 0.5, 0.0, 0.0), # log_operation
],
structural_features={
"has_validation": True,
"has_logging": True,
},
actual_profile=LJPWProfile(0.25, 0.25, 0.25, 0.25), # Inferred from secure_subtract
description="secure_subtract function",
)
)
# Example 5: secure_multiply (Level 1)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.0, 0.0, 0.0, 0.0), # multiply_simple
LJPWProfile(0.0, 1.0, 0.0, 0.0), # validate_numeric
LJPWProfile(0.5, 0.5, 0.0, 0.0), # log_operation
],
structural_features={
"has_validation": True,
"has_logging": True,
},
actual_profile=LJPWProfile(0.25, 0.25, 0.25, 0.25), # Inferred from secure_multiply
description="secure_multiply function",
)
)
# Example 6: secure_divide (Level 1)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.0, 0.5, 0.5, 0.0), # divide_simple
LJPWProfile(0.0, 1.0, 0.0, 0.0), # validate_numeric
LJPWProfile(0.5, 0.5, 0.0, 0.0), # log_operation
],
structural_features={
"has_validation": True,
"has_logging": True,
},
actual_profile=LJPWProfile(0.25, 0.25, 0.25, 0.25), # Inferred from secure_divide
description="secure_divide function",
)
)
# Example 7: simple_add (Level 1)
# Just wraps add_simple with minimal overhead
examples.append(
CompositionExample(
components=[
LJPWProfile(1.0, 0.0, 0.0, 0.0), # add_simple primitive
],
structural_features={},
actual_profile=LJPWProfile(0.333, 0.333, 0.333, 0.0), # Actual from harmonizer
description="simple_add function",
)
)
# Example 8: simple_multiply (Level 1)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.0, 0.0, 0.0, 0.0), # multiply_simple primitive
],
structural_features={},
actual_profile=LJPWProfile(0.333, 0.333, 0.333, 0.0), # Actual from harmonizer
description="simple_multiply function",
)
)
# Example 9: Primitive aggregation test (subtract + multiply)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.0, 0.0, 0.0, 0.0), # subtract_simple
LJPWProfile(0.0, 0.0, 0.0, 0.0), # multiply_simple
],
structural_features={},
actual_profile=LJPWProfile(0.0, 0.0, 0.0, 0.0), # Both are zero, result is zero
description="Zero primitive aggregation",
)
)
# Example 10: Mixed primitive aggregation (add + divide)
examples.append(
CompositionExample(
components=[
LJPWProfile(1.0, 0.0, 0.0, 0.0), # add_simple
LJPWProfile(0.0, 0.5, 0.5, 0.0), # divide_simple
],
structural_features={},
actual_profile=LJPWProfile(0.5, 0.25, 0.25, 0.0), # Average
description="Mixed primitive aggregation",
)
)
# Example 11: Primitive composition (validate + log)
examples.append(
CompositionExample(
components=[
LJPWProfile(0.0, 1.0, 0.0, 0.0), # validate_numeric
LJPWProfile(0.5, 0.5, 0.0, 0.0), # log_operation
],
structural_features={},
actual_profile=LJPWProfile(0.25, 0.75, 0.0, 0.0), # Average
description="Validation + Logging composition",
)
)
# Example 12: Full primitive set aggregation
examples.append(
CompositionExample(
components=[
LJPWProfile(1.0, 0.0, 0.0, 0.0), # add_simple
LJPWProfile(0.0, 1.0, 0.0, 0.0), # validate_numeric
LJPWProfile(0.5, 0.5, 0.0, 0.0), # log_operation
LJPWProfile(0.0, 0.5, 0.5, 0.0), # divide_simple
],
structural_features={},
actual_profile=LJPWProfile(0.375, 0.5, 0.125, 0.0), # Average
description="Full primitive set",
)
)
# Example 13: Calculator with state (inferred from pattern)
# This is a hypothetical example for a working stateful calculator
examples.append(
CompositionExample(
components=[
LJPWProfile(0.25, 0.25, 0.25, 0.25), # secure_add
LJPWProfile(0.25, 0.25, 0.25, 0.25), # secure_multiply
],
structural_features={
"has_state": True,
},
actual_profile=LJPWProfile(0.25, 0.35, 0.25, 0.40), # State adds J and W
description="StatefulCalculator (inferred)",
)
)
return examples
def evaluate_constants(constants: CouplingConstants, examples: List[CompositionExample]) -> float:
"""
Evaluate coupling constants on training examples.
Returns: Mean squared error across all examples.
"""
predictor = CompositionPredictor(constants)
total_error = 0.0
for example in examples:
predicted = predictor.predict(example.components, example.structural_features)
actual = example.actual_profile
error = predicted.distance_to(actual)
total_error += error ** 2
mse = total_error / len(examples)
return mse
def optimize_constants(examples: List[CompositionExample]) -> CouplingConstants:
"""
Optimize coupling constants using scipy.optimize.
Uses L-BFGS-B algorithm with bounds to ensure reasonable values.
"""
try:
from scipy.optimize import minimize
except ImportError:
print("Warning: scipy not available. Using manual calibration instead.")
return manual_calibration(examples)
# Initial guess (current theoretical values)
initial = CouplingConstants()
x0 = initial.to_vector()
# Bounds: coupling constants in [0.8, 1.5], bonuses in [0.0, 0.3]
bounds = [
(0.8, 1.5), # κ_LJ
(0.8, 1.5), # κ_LP
(0.8, 1.5), # κ_JL
(0.8, 1.5), # κ_WL
(0.0, 0.3), # bonus_docstring
(0.0, 0.2), # bonus_type_hints
(0.0, 0.2), # bonus_error_handling
(0.0, 0.3), # bonus_logging
(0.0, 0.3), # bonus_testing
(0.0, 0.3), # bonus_state
(0.0, 0.3), # bonus_history
(0.0, 0.2), # bonus_validation
]
# Objective function
def objective(x):
constants = CouplingConstants.from_vector(x)
return evaluate_constants(constants, examples)
print("Optimizing coupling constants...")
result = minimize(objective, x0, method="L-BFGS-B", bounds=bounds)
if result.success:
print(f"✅ Optimization converged! Final MSE: {result.fun:.4f}")
else:
print(f"⚠️ Optimization did not fully converge. MSE: {result.fun:.4f}")
return CouplingConstants.from_vector(result.x)
def manual_calibration(examples: List[CompositionExample]) -> CouplingConstants:
"""
Manual calibration based on empirical findings.
Reduces coupling constants and bonuses by ~30% based on observed overestimation.
"""
print("Performing manual calibration (reducing by 30%)...")
return CouplingConstants(
# Reduce coupling constants by ~25%
κ_LJ=0.90, # was 1.2
κ_LP=0.98, # was 1.3
κ_JL=0.90, # was 1.2
κ_WL=0.85, # was 1.1
# Reduce structural bonuses by ~30%
bonus_docstring=0.07, # was 0.10
bonus_type_hints=0.035, # was 0.05
bonus_error_handling=0.056, # was 0.08
bonus_logging=0.084, # was 0.12
bonus_testing=0.105, # was 0.15
bonus_state=0.105, # was 0.15
bonus_history=0.14, # was 0.20
bonus_validation=0.07, # was 0.10
)
def main():
print("=" * 80)
print("COMPOSITION RULE CALIBRATION")
print("=" * 80)
print()
# Check harmonizer availability
if not HARMONIZER_AVAILABLE:
print("⚠️ Warning: Real harmonizer not available.")
print(" Calibration will use hardcoded empirical measurements.")
print()
# Collect training data
print("Step 1: Collecting training data from empirical validation...")
examples = collect_training_data()
print(f" Collected {len(examples)} training examples")
print()
# Evaluate current constants
print("Step 2: Evaluating current (theoretical) constants...")
current_constants = CouplingConstants()
current_mse = evaluate_constants(current_constants, examples)
print(f" Current MSE: {current_mse:.4f}")
print()
# Display current predictions vs actual
print("Current Predictions vs Actual:")
print("-" * 80)
predictor = CompositionPredictor(current_constants)
for example in examples:
predicted = predictor.predict(example.components, example.structural_features)
actual = example.actual_profile
error = predicted.distance_to(actual)
print(f" {example.description}:")
print(f" Predicted: {predicted}")
print(f" Actual: {actual}")
print(f" Error: {error:.4f}")
print()
# Optimize constants
print("Step 3: Optimizing coupling constants...")
optimized_constants = optimize_constants(examples)
optimized_mse = evaluate_constants(optimized_constants, examples)
print()
# Compare results
print("=" * 80)
print("CALIBRATION RESULTS")
print("=" * 80)
print()
print(f"Current MSE: {current_mse:.4f}")
print(f"Optimized MSE: {optimized_mse:.4f}")
print(f"Improvement: {(1 - optimized_mse / current_mse) * 100:.1f}%")
print()
print("Optimized Constants:")
print(f" κ_LJ (Love→Justice): {current_constants.κ_LJ:.3f} → {optimized_constants.κ_LJ:.3f}")
print(f" κ_LP (Love→Power): {current_constants.κ_LP:.3f} → {optimized_constants.κ_LP:.3f}")
print(f" κ_JL (Justice→Love): {current_constants.κ_JL:.3f} → {optimized_constants.κ_JL:.3f}")
print(f" κ_WL (Wisdom→Love): {current_constants.κ_WL:.3f} → {optimized_constants.κ_WL:.3f}")
print()
print("Optimized Bonuses:")
print(f" Docstring: {current_constants.bonus_docstring:.3f} → {optimized_constants.bonus_docstring:.3f}")
print(f" Type hints: {current_constants.bonus_type_hints:.3f} → {optimized_constants.bonus_type_hints:.3f}")
print(f" Error handling: {current_constants.bonus_error_handling:.3f} → {optimized_constants.bonus_error_handling:.3f}")
print(f" Logging: {current_constants.bonus_logging:.3f} → {optimized_constants.bonus_logging:.3f}")
print(f" Testing: {current_constants.bonus_testing:.3f} → {optimized_constants.bonus_testing:.3f}")
print(f" State: {current_constants.bonus_state:.3f} → {optimized_constants.bonus_state:.3f}")
print(f" History: {current_constants.bonus_history:.3f} → {optimized_constants.bonus_history:.3f}")
print(f" Validation: {current_constants.bonus_validation:.3f} → {optimized_constants.bonus_validation:.3f}")
print()
# Show improved predictions
print("Improved Predictions vs Actual:")
print("-" * 80)
improved_predictor = CompositionPredictor(optimized_constants)
for example in examples:
predicted = improved_predictor.predict(example.components, example.structural_features)
actual = example.actual_profile
error = predicted.distance_to(actual)
print(f" {example.description}:")
print(f" Predicted: {predicted}")
print(f" Actual: {actual}")
print(f" Error: {error:.4f}")
print()
print("=" * 80)
print("NEXT STEPS")
print("=" * 80)
print()
print("1. Update experiment files with calibrated constants")
print("2. Re-run experiments to validate improved accuracy")
print("3. Collect more training examples for further refinement")
print("4. Consider machine learning approach for automatic calibration")
print()
return 0
if __name__ == "__main__":
sys.exit(main())