tostido's picture
download
raw
42.1 kB
# Violation Pressure Calculation - Phase 0.2 Implementation
# Version 1.0 - Mathematical Quantification of Identity Incompleteness
"""
Violation Pressure Calculation implementing the mathematical quantification of identity incompleteness
that drives recursive necessity in the Djinn Kernel.
Core Formula: VP_total = Σ(|actual - center| / (radius * compression))
This creates the mathematical pressure that drives the system toward fixed-point completion.
"""
from dataclasses import dataclass, field
from typing import Dict, Tuple, List, Optional, Any
from enum import Enum
import math
import logging
import json
from datetime import datetime
from pathlib import Path
from event_driven_coordination import ViolationPressureEvent
class ViolationClass(Enum):
"""Classification of violation pressure levels"""
VP0_FULLY_LAWFUL = "VP0" # 0.00 - 0.25: Fully lawful recursion
VP1_STABLE_DRIFT = "VP1" # 0.25 - 0.50: Stable drift, continue with logging
VP2_INSTABILITY = "VP2" # 0.50 - 0.75: Instability pressure, arbitration review
VP3_CRITICAL_DIVERGENCE = "VP3" # 0.75 - 0.99: Critical divergence, forbidden zone authorization
VP4_COLLAPSE_THRESHOLD = "VP4" # ≥ 1.00: Collapse threshold, hard recursion termination
@dataclass
class StabilityEnvelope:
"""
Mathematical envelope defining trait stability boundaries.
Core component of violation pressure calculation.
"""
center: float = 0.5 # Stability center [0.0, 1.0]
radius: float = 0.25 # Allowable deviation range
compression_factor: float = 1.0 # Stability enforcement strength
def __post_init__(self):
# Ensure mathematical consistency
assert 0.0 <= self.center <= 1.0, "Stability center must be [0.0, 1.0]"
assert 0.0 < self.radius <= 0.5, "Radius must be (0.0, 0.5]"
assert self.compression_factor > 0.0, "Compression factor must be positive"
class VPDiagnostics:
"""
Diagnostic logging for violation pressure calculations.
Provides detailed breakdown of VP components without changing calculation logic.
"""
def __init__(self, enabled: bool = False, log_file: Optional[str] = None):
self.enabled = enabled
self.log_file = log_file or (Path(__file__).parent.parent / 'data' / 'logs' / 'vp_diagnostics.log')
self.logger = None
if self.enabled:
self._setup_logger()
def _setup_logger(self):
"""Setup diagnostic logger"""
# Ensure log directory exists
self.log_file.parent.mkdir(parents=True, exist_ok=True)
# Create logger
self.logger = logging.getLogger('vp_diagnostics')
self.logger.setLevel(logging.DEBUG)
# Remove existing handlers to avoid duplicates
self.logger.handlers.clear()
# File handler
file_handler = logging.FileHandler(self.log_file, encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
# Format: timestamp|component|data
formatter = logging.Formatter(
'%(asctime)s|%(name)s|%(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(formatter)
self.logger.addHandler(file_handler)
self.logger.propagate = False
def log_trait_breakdown(self, trait_name: str, trait_value: float, envelope: StabilityEnvelope,
deviation: float, trait_vp: float, normalization_factor: float):
"""Log detailed breakdown for a single trait"""
if not self.enabled:
return
if not self.logger:
# Logger not initialized - this shouldn't happen if enabled=True
import logging
logging.warning(f"[VPDiagnostics] Logger not initialized but enabled=True for trait {trait_name}")
return
breakdown = {
'trait_name': trait_name,
'trait_value': trait_value,
'envelope_center': envelope.center,
'envelope_radius': envelope.radius,
'compression_factor': envelope.compression_factor,
'deviation': deviation,
'trait_vp': trait_vp,
'normalization_factor': normalization_factor,
'normalized_radius': envelope.radius * envelope.compression_factor
}
self.logger.debug(f"trait_breakdown|{json.dumps(breakdown)}")
def log_calculation_summary(self, trait_payload: Dict[str, float], total_vp: float,
per_trait_breakdown: Dict[str, float], source_identity: Optional[str] = None):
"""Log summary of VP calculation"""
if not self.enabled:
return
if not self.logger:
# Logger not initialized - this shouldn't happen if enabled=True
import logging
logging.warning(f"[VPDiagnostics] Logger not initialized but enabled=True for calculation summary")
return
summary = {
'timestamp': datetime.utcnow().isoformat() + 'Z',
'source_identity': source_identity,
'trait_count': len(trait_payload),
'total_vp': total_vp,
'per_trait_breakdown': per_trait_breakdown,
'average_trait_vp': sum(per_trait_breakdown.values()) / len(per_trait_breakdown) if per_trait_breakdown else 0.0,
'max_trait_vp': max(per_trait_breakdown.values()) if per_trait_breakdown else 0.0,
'min_trait_vp': min(per_trait_breakdown.values()) if per_trait_breakdown else 0.0
}
self.logger.info(f"calculation_summary|{json.dumps(summary)}")
def get_vp_diagnostics(self, trait_payload: Dict[str, float],
stability_envelopes: Dict[str, StabilityEnvelope]) -> Dict[str, Any]:
"""
Decompose VP calculation into diagnostic components.
Does not change calculation - just analyzes what was computed.
"""
diagnostics = {
'trait_analysis': {},
'envelope_analysis': {},
'summary': {}
}
for trait_name, trait_value in trait_payload.items():
envelope = stability_envelopes.get(trait_name, StabilityEnvelope())
deviation = abs(trait_value - envelope.center)
normalized_radius = envelope.radius * envelope.compression_factor
trait_vp = deviation / normalized_radius if normalized_radius > 0 else (float('inf') if deviation > 0 else 0.0)
trait_vp = max(0.0, min(10.0, trait_vp))
diagnostics['trait_analysis'][trait_name] = {
'trait_value': trait_value,
'envelope_center': envelope.center,
'deviation': deviation,
'normalized_radius': normalized_radius,
'trait_vp': trait_vp,
'vp_contribution_ratio': trait_vp / len(trait_payload) if trait_payload else 0.0
}
diagnostics['envelope_analysis'][trait_name] = {
'center': envelope.center,
'radius': envelope.radius,
'compression_factor': envelope.compression_factor,
'effective_range': [envelope.center - envelope.radius, envelope.center + envelope.radius]
}
if trait_payload:
total_vp_sum = sum(diagnostics['trait_analysis'][t]['trait_vp']
for t in trait_payload.keys())
normalized_total = min(1.0, total_vp_sum / len(trait_payload))
diagnostics['summary'] = {
'trait_count': len(trait_payload),
'raw_vp_sum': total_vp_sum,
'normalized_vp': normalized_total,
'normalization_factor': len(trait_payload),
'dominant_trait': max(trait_payload.keys(),
key=lambda t: diagnostics['trait_analysis'][t]['trait_vp']) if trait_payload else None
}
return diagnostics
class VPStabilizer:
"""
Stabilizes violation pressure values to prevent immediate jumps to maximum.
Uses weighted moving average and jump limiting to smooth VP transitions.
"""
def __init__(self, history_size: int = 10, max_jump: float = 0.1, smoothing_factor: float = 0.3):
self.history_size = history_size
self.max_jump = max_jump
self.smoothing_factor = smoothing_factor
self.vp_history = []
self.last_vp = None
def stabilize(self, raw_vp: float) -> float:
"""
Stabilize a raw VP value using smoothing and jump limiting.
Args:
raw_vp: Raw violation pressure value [0.0, 1.0]
Returns:
Stabilized VP value [0.0, 1.0]
"""
# Initialize if this is the first value
if self.last_vp is None:
self.last_vp = raw_vp
self.vp_history.append(raw_vp)
return raw_vp
# Calculate jump size
jump = raw_vp - self.last_vp
# Apply jump limiting
if abs(jump) > self.max_jump:
if jump > 0:
stabilized_vp = self.last_vp + self.max_jump
else:
stabilized_vp = self.last_vp - self.max_jump
else:
stabilized_vp = raw_vp
# Apply weighted moving average smoothing
# EMA: new_value = smoothing_factor * current + (1 - smoothing_factor) * previous
smoothed_vp = self.smoothing_factor * stabilized_vp + (1 - self.smoothing_factor) * self.last_vp
# Clamp to valid range
smoothed_vp = max(0.0, min(1.0, smoothed_vp))
# Update history
self.vp_history.append(smoothed_vp)
if len(self.vp_history) > self.history_size:
self.vp_history.pop(0)
# Update last VP
self.last_vp = smoothed_vp
return smoothed_vp
def reset(self):
"""Reset stabilizer state"""
self.vp_history = []
self.last_vp = None
def get_history(self) -> List[float]:
"""Get VP history"""
return self.vp_history.copy()
class VPComponentCalculator:
"""
Calculates violation pressure as weighted components to identify saturation sources.
Breaks VP into trait_divergence, network_coherence, phase_mismatch, evolution_pressure, quantum_entropy.
"""
def __init__(self, component_weights: Optional[Dict[str, float]] = None):
"""
Initialize component calculator with weights.
Args:
component_weights: Dictionary of component_name -> weight (must sum to 1.0)
"""
default_weights = {
'trait_divergence': 0.25,
'network_coherence': 0.20,
'phase_mismatch': 0.15,
'evolution_pressure': 0.20,
'quantum_entropy': 0.20
}
self.component_weights = component_weights or default_weights
# Normalize weights to sum to 1.0
total_weight = sum(self.component_weights.values())
if total_weight > 0:
self.component_weights = {k: v / total_weight for k, v in self.component_weights.items()}
def sigmoid(self, x: float, k: float = 5.0) -> float:
"""Sigmoid function for smoothing: prevents single component from dominating"""
return 1.0 / (1.0 + math.exp(-k * (x - 0.5)))
def calculate_components(self, trait_payload: Dict[str, float],
stability_envelopes: Dict[str, StabilityEnvelope]) -> Dict[str, float]:
"""
Calculate component pressures from trait payload.
Returns:
Dictionary of component_name -> component_vp [0.0, 1.0]
"""
components = {
'trait_divergence': 0.0,
'network_coherence': 0.0,
'phase_mismatch': 0.0,
'evolution_pressure': 0.0,
'quantum_entropy': 0.0
}
if not trait_payload:
return components
# Group traits by category for component calculation
network_traits = ['organism_count', 'modularity', 'clustering_coefficient', 'average_path_length']
prosocial_traits = ['intimacy', 'commitment', 'caregiving', 'attunement', 'lineagepreference']
meta_traits = ['violationpressure', 'completionpressure', 'convergencestability', 'reflectionindex']
# Calculate trait_divergence: average deviation from stability centers
divergences = []
for trait_name, trait_value in trait_payload.items():
envelope = stability_envelopes.get(trait_name, StabilityEnvelope())
deviation = abs(trait_value - envelope.center)
normalized_radius = envelope.radius * envelope.compression_factor
if normalized_radius > 0:
divergence = min(1.0, deviation / normalized_radius)
divergences.append(divergence)
components['trait_divergence'] = sum(divergences) / len(divergences) if divergences else 0.0
# Calculate network_coherence: coherence of network traits
network_values = [trait_payload.get(t, 0.0) for t in network_traits if t in trait_payload]
if network_values:
# Measure variance as inverse of coherence
mean_network = sum(network_values) / len(network_values)
variance = sum((v - mean_network) ** 2 for v in network_values) / len(network_values)
components['network_coherence'] = min(1.0, variance * 4.0) # Scale variance to [0,1]
# Calculate phase_mismatch: mismatch in prosocial traits
prosocial_values = [trait_payload.get(t, 0.0) for t in prosocial_traits if t in trait_payload]
if prosocial_values:
# Measure spread as phase mismatch
min_prosocial = min(prosocial_values)
max_prosocial = max(prosocial_values)
components['phase_mismatch'] = max_prosocial - min_prosocial
# Calculate evolution_pressure: pressure from meta-traits
meta_values = [trait_payload.get(t, 0.0) for t in meta_traits if t in trait_payload]
if meta_values:
# Average of meta-traits as evolution pressure
components['evolution_pressure'] = sum(meta_values) / len(meta_values)
# Calculate quantum_entropy: entropy in trait distribution
if len(trait_payload) > 1:
# Shannon entropy of normalized trait values
trait_values = list(trait_payload.values())
total = sum(abs(v) for v in trait_values)
if total > 0:
probabilities = [abs(v) / total for v in trait_values]
entropy = -sum(p * math.log(p + 1e-10) for p in probabilities if p > 0)
max_entropy = math.log(len(trait_values))
components['quantum_entropy'] = entropy / max_entropy if max_entropy > 0 else 0.0
# Apply sigmoid smoothing to prevent domination
for component_name in components:
components[component_name] = self.sigmoid(components[component_name])
return components
def combine_components(self, component_vps: Dict[str, float]) -> float:
"""
Combine component VPs using weighted geometric mean.
Geometric mean prevents single component from dominating:
total = (∏(component_i ^ weight_i)) ^ (1 / sum(weights))
"""
if not component_vps:
return 0.0
# Weighted geometric mean
log_sum = 0.0
weight_sum = 0.0
for component_name, component_vp in component_vps.items():
weight = self.component_weights.get(component_name, 0.0)
if weight > 0 and component_vp > 0:
log_sum += weight * math.log(component_vp + 1e-10)
weight_sum += weight
if weight_sum > 0:
geometric_mean = math.exp(log_sum / weight_sum)
return max(0.0, min(1.0, geometric_mean))
return 0.0
class AdaptiveThresholdManager:
"""
Manages adaptive thresholds based on system phase.
Adjusts VP classification thresholds dynamically based on phase and historical VP variance.
"""
def __init__(self):
self.base_thresholds = {
ViolationClass.VP0_FULLY_LAWFUL: 0.25,
ViolationClass.VP1_STABLE_DRIFT: 0.50,
ViolationClass.VP2_INSTABILITY: 0.75,
ViolationClass.VP3_CRITICAL_DIVERGENCE: 1.00,
ViolationClass.VP4_COLLAPSE_THRESHOLD: float('inf')
}
# Phase-specific threshold adjustments
self.phase_adjustments = {
'genesis': {
'sensitivity_multiplier': 0.6, # More sensitive (lower thresholds)
'base_adjustments': {
ViolationClass.VP0_FULLY_LAWFUL: 0.15, # 0.0-0.15 instead of 0.0-0.25
ViolationClass.VP1_STABLE_DRIFT: 0.35, # 0.15-0.35 instead of 0.25-0.50
ViolationClass.VP2_INSTABILITY: 0.55, # 0.35-0.55 instead of 0.50-0.75
ViolationClass.VP3_CRITICAL_DIVERGENCE: 0.80, # 0.55-0.80 instead of 0.75-1.00
}
},
'sovereign': {
'sensitivity_multiplier': 1.2, # Less sensitive (higher thresholds)
'base_adjustments': {
ViolationClass.VP0_FULLY_LAWFUL: 0.20, # 0.0-0.20 instead of 0.0-0.25
ViolationClass.VP1_STABLE_DRIFT: 0.40, # 0.20-0.40 instead of 0.25-0.50
ViolationClass.VP2_INSTABILITY: 0.65, # 0.40-0.65 instead of 0.50-0.75
ViolationClass.VP3_CRITICAL_DIVERGENCE: 0.90, # 0.65-0.90 instead of 0.75-1.00
}
}
}
def adjust_thresholds_for_phase(self, phase: str, historical_vps: List[float]) -> Dict[ViolationClass, float]:
"""
Adjust thresholds based on phase and historical VP variance.
Args:
phase: System phase ('genesis' or 'sovereign')
historical_vps: List of recent VP values (last 100 recommended)
Returns:
Dictionary of ViolationClass -> threshold value
"""
# Start with base thresholds
adjusted_thresholds = self.base_thresholds.copy()
# Get phase-specific adjustments
phase_config = self.phase_adjustments.get(phase.lower(), {})
base_adjustments = phase_config.get('base_adjustments', {})
# Apply base phase adjustments
for vp_class, threshold in base_adjustments.items():
adjusted_thresholds[vp_class] = threshold
# Calculate variance-based adjustments if historical data available
if len(historical_vps) >= 10:
variance = self._calculate_variance(historical_vps)
mean_vp = sum(historical_vps) / len(historical_vps)
# Adjust sensitivity based on variance
# High variance = more stability needed = lower thresholds
# Low variance = can tolerate more = higher thresholds
variance_factor = 1.0 - (variance * 0.5) # Reduce thresholds if high variance
variance_factor = max(0.7, min(1.3, variance_factor)) # Clamp to reasonable range
# Adjust thresholds by variance factor
for vp_class in [ViolationClass.VP0_FULLY_LAWFUL, ViolationClass.VP1_STABLE_DRIFT,
ViolationClass.VP2_INSTABILITY, ViolationClass.VP3_CRITICAL_DIVERGENCE]:
if vp_class in adjusted_thresholds and adjusted_thresholds[vp_class] != float('inf'):
adjusted_thresholds[vp_class] *= variance_factor
# Additional adjustment based on mean VP
# If mean VP is already high, be more sensitive to prevent further increases
if mean_vp > 0.5:
mean_adjustment = 0.9 # Reduce thresholds by 10%
for vp_class in [ViolationClass.VP0_FULLY_LAWFUL, ViolationClass.VP1_STABLE_DRIFT,
ViolationClass.VP2_INSTABILITY, ViolationClass.VP3_CRITICAL_DIVERGENCE]:
if vp_class in adjusted_thresholds and adjusted_thresholds[vp_class] != float('inf'):
adjusted_thresholds[vp_class] *= mean_adjustment
return adjusted_thresholds
def _calculate_variance(self, values: List[float]) -> float:
"""Calculate variance of VP values"""
if len(values) < 2:
return 0.0
mean = sum(values) / len(values)
variance = sum((x - mean) ** 2 for x in values) / len(values)
return variance
def classify_with_adaptive_thresholds(self, vp: float, phase: str, historical_vps: List[float]) -> ViolationClass:
"""
Classify VP using phase-adaptive thresholds.
Args:
vp: Violation pressure value [0.0, 1.0]
phase: System phase ('genesis' or 'sovereign')
historical_vps: List of recent VP values
Returns:
ViolationClass classification
"""
thresholds = self.adjust_thresholds_for_phase(phase, historical_vps)
if vp < thresholds[ViolationClass.VP0_FULLY_LAWFUL]:
return ViolationClass.VP0_FULLY_LAWFUL
elif vp < thresholds[ViolationClass.VP1_STABLE_DRIFT]:
return ViolationClass.VP1_STABLE_DRIFT
elif vp < thresholds[ViolationClass.VP2_INSTABILITY]:
return ViolationClass.VP2_INSTABILITY
elif vp < thresholds[ViolationClass.VP3_CRITICAL_DIVERGENCE]:
return ViolationClass.VP3_CRITICAL_DIVERGENCE
else:
return ViolationClass.VP4_COLLAPSE_THRESHOLD
class ViolationMonitor:
"""
Calculates violation pressure - the mathematical driving force of recursion.
Violation pressure quantifies how far traits deviate from their stability centers,
creating mathematical necessity for recursive correction through:
- Convergence (lawful primitive recursion)
- Divergence (μ-recursion in Forbidden Zone)
- Collapse (entropy compression via pruning)
"""
def __init__(self, event_publisher=None, diagnostics_enabled: bool = False,
stabilization_enabled: bool = False, max_vp_jump: float = 0.1,
smoothing_factor: float = 0.3, component_decomposition_enabled: bool = False,
component_weights: Optional[Dict[str, float]] = None,
adaptive_thresholds_enabled: bool = False,
max_history_size: int = 1000):
self.event_publisher = event_publisher
self.vp_history = []
self.max_history_size = max_history_size # Bound history to prevent memory leaks
self.stability_envelopes = {}
# Initialize diagnostics (disabled by default for backward compatibility)
self.diagnostics = VPDiagnostics(enabled=diagnostics_enabled)
# Initialize stabilizer (disabled by default for backward compatibility)
self.stabilization_enabled = stabilization_enabled
self.stabilizer = VPStabilizer(
history_size=10,
max_jump=max_vp_jump,
smoothing_factor=smoothing_factor
) if stabilization_enabled else None
# Initialize component calculator (disabled by default for backward compatibility)
self.component_decomposition_enabled = component_decomposition_enabled
self.component_calculator = VPComponentCalculator(component_weights=component_weights) if component_decomposition_enabled else None
# Initialize adaptive threshold manager (disabled by default for backward compatibility)
self.adaptive_thresholds_enabled = adaptive_thresholds_enabled
self.adaptive_threshold_manager = AdaptiveThresholdManager() if adaptive_thresholds_enabled else None
# Initialize default stability envelopes for common traits
self._initialize_default_envelopes()
def _initialize_default_envelopes(self):
"""Initialize default stability envelopes for mathematical consistency"""
# Mathematical meta-traits
self.stability_envelopes["violationpressure"] = StabilityEnvelope(
center=0.0, radius=0.25, compression_factor=2.0
)
self.stability_envelopes["completionpressure"] = StabilityEnvelope(
center=0.0, radius=0.3, compression_factor=1.5
)
self.stability_envelopes["convergencestability"] = StabilityEnvelope(
center=0.8, radius=0.15, compression_factor=1.2
)
self.stability_envelopes["reflectionindex"] = StabilityEnvelope(
center=0.7, radius=0.2, compression_factor=1.0
)
# Prosocial traits (love metrics)
self.stability_envelopes["intimacy"] = StabilityEnvelope(
center=0.6, radius=0.3, compression_factor=0.8
)
self.stability_envelopes["commitment"] = StabilityEnvelope(
center=0.7, radius=0.25, compression_factor=1.1
)
self.stability_envelopes["caregiving"] = StabilityEnvelope(
center=0.65, radius=0.3, compression_factor=0.9
)
self.stability_envelopes["attunement"] = StabilityEnvelope(
center=0.6, radius=0.25, compression_factor=1.0
)
self.stability_envelopes["lineagepreference"] = StabilityEnvelope(
center=0.55, radius=0.35, compression_factor=0.7
)
# FIX: Reality Simulator network traits (normalized to [0.0, 1.0])
# These traits come from the Reality Simulator network component
# Normalization: organism_count / 1000, average_path_length / 10
self.stability_envelopes["organism_count"] = StabilityEnvelope(
center=0.4, # Target 400 organisms (normalized: 400/1000 = 0.4)
radius=0.2, # Allow 200-600 organisms (0.2-0.6 normalized range)
compression_factor=1.0
)
self.stability_envelopes["modularity"] = StabilityEnvelope(
center=0.3, # Target moderate modularity (0.3)
radius=0.2, # Allow 0.1-0.5 range (low to moderate modularity)
compression_factor=1.0
)
self.stability_envelopes["clustering_coefficient"] = StabilityEnvelope(
center=0.2, # Target moderate clustering (0.2)
radius=0.15, # Allow 0.05-0.35 range
compression_factor=1.0
)
self.stability_envelopes["average_path_length"] = StabilityEnvelope(
center=0.5, # Target path length of 5 (normalized: 5/10 = 0.5)
radius=0.3, # Allow 2-8 path length (0.2-0.8 normalized range)
compression_factor=1.0
)
def compute_violation_pressure(self, trait_payload: Dict[str, float],
source_identity: Optional[str] = None,
system_phase: Optional[str] = None) -> Tuple[float, Dict[str, float]]:
"""
Calculate violation pressure for trait payload.
Args:
trait_payload: Dictionary of trait_name -> trait_value pairs
source_identity: Optional UUID of the identity being evaluated
Returns:
Tuple of (total_vp, per_trait_breakdown)
"""
total_vp = 0.0
per_trait_breakdown = {}
for trait_name, trait_value in trait_payload.items():
# Get stability envelope for this trait
envelope = self.stability_envelopes.get(trait_name, StabilityEnvelope())
# Calculate deviation for diagnostics
deviation = abs(trait_value - envelope.center)
normalized_radius = envelope.radius * envelope.compression_factor
# Calculate individual trait violation pressure
trait_vp = self._calculate_trait_violation_pressure(trait_value, envelope)
# Log diagnostic breakdown if enabled
if self.diagnostics.enabled:
normalization_factor = len(trait_payload) if trait_payload else 1.0
self.diagnostics.log_trait_breakdown(
trait_name, trait_value, envelope, deviation, trait_vp, normalization_factor
)
per_trait_breakdown[trait_name] = trait_vp
total_vp += trait_vp
# Normalize total VP to [0.0, 1.0] range
if trait_payload:
total_vp = min(1.0, total_vp / len(trait_payload))
# Apply stabilization if enabled (before logging and classification)
if self.stabilization_enabled and self.stabilizer:
total_vp = self.stabilizer.stabilize(total_vp)
# Log calculation summary if diagnostics enabled
if self.diagnostics.enabled:
self.diagnostics.log_calculation_summary(
trait_payload, total_vp, per_trait_breakdown, source_identity
)
# Classify violation pressure (use adaptive thresholds if enabled)
if self.adaptive_thresholds_enabled and self.adaptive_threshold_manager and system_phase:
# Get recent VP history for adaptive threshold calculation
recent_vps = [entry["total_vp"] for entry in self.vp_history[-100:]]
classification = self.adaptive_threshold_manager.classify_with_adaptive_thresholds(
total_vp, system_phase, recent_vps
)
else:
classification = self._classify_violation_pressure(total_vp)
# Create violation pressure event
vp_event = ViolationPressureEvent(
total_vp=total_vp,
breakdown=per_trait_breakdown,
classification=classification.value,
source_identity=source_identity
)
# Publish event for system coordination
if self.event_publisher:
self.event_publisher.publish(vp_event)
# Record in history
self.vp_history.append({
"timestamp": datetime.utcnow().isoformat() + "Z",
"total_vp": total_vp,
"classification": classification.value,
"source_identity": source_identity,
"trait_count": len(trait_payload)
})
# Bound history size to prevent memory leaks
if len(self.vp_history) > self.max_history_size:
self.vp_history = self.vp_history[-self.max_history_size:]
return total_vp, per_trait_breakdown
def get_vp_diagnostics(self, trait_payload: Dict[str, float]) -> Dict[str, Any]:
"""
Get detailed diagnostic breakdown of VP calculation without changing the calculation.
Useful for analyzing what's driving VP saturation.
Returns:
Dictionary with trait_analysis, envelope_analysis, and summary
"""
return self.diagnostics.get_vp_diagnostics(trait_payload, self.stability_envelopes)
def compute_violation_pressure_decomposed(self, trait_payload: Dict[str, float],
source_identity: Optional[str] = None) -> Tuple[float, Dict[str, float], Dict[str, float]]:
"""
Calculate violation pressure with component decomposition.
Returns breakdown showing which components are driving high VP.
Args:
trait_payload: Dictionary of trait_name -> trait_value pairs
source_identity: Optional UUID of the identity being evaluated
Returns:
Tuple of (total_vp, per_trait_breakdown, component_breakdown)
- total_vp: Combined VP from weighted components [0.0, 1.0]
- per_trait_breakdown: Individual trait VPs
- component_breakdown: Component-level VPs (trait_divergence, network_coherence, etc.)
"""
if not self.component_calculator:
# Fallback to standard calculation if decomposition not enabled
total_vp, per_trait_breakdown = self.compute_violation_pressure(trait_payload, source_identity)
return total_vp, per_trait_breakdown, {}
# Calculate component pressures
component_breakdown = self.component_calculator.calculate_components(
trait_payload, self.stability_envelopes
)
# Combine components using weighted geometric mean
total_vp = self.component_calculator.combine_components(component_breakdown)
# Calculate per-trait breakdown for compatibility
per_trait_breakdown = {}
for trait_name, trait_value in trait_payload.items():
envelope = self.stability_envelopes.get(trait_name, StabilityEnvelope())
trait_vp = self._calculate_trait_violation_pressure(trait_value, envelope)
per_trait_breakdown[trait_name] = trait_vp
# Apply stabilization if enabled
if self.stabilization_enabled and self.stabilizer:
total_vp = self.stabilizer.stabilize(total_vp)
# Classify violation pressure
classification = self._classify_violation_pressure(total_vp)
# Create violation pressure event (same as standard calculation)
vp_event = ViolationPressureEvent(
total_vp=total_vp,
breakdown=per_trait_breakdown,
classification=classification.value,
source_identity=source_identity
)
# Publish event for system coordination
if self.event_publisher:
self.event_publisher.publish(vp_event)
# Record in history
self.vp_history.append({
"timestamp": datetime.utcnow().isoformat() + "Z",
"total_vp": total_vp,
"classification": classification.value,
"source_identity": source_identity,
"trait_count": len(trait_payload),
"component_breakdown": component_breakdown
})
# Bound history size to prevent memory leaks
if len(self.vp_history) > self.max_history_size:
self.vp_history = self.vp_history[-self.max_history_size:]
return total_vp, per_trait_breakdown, component_breakdown
def _calculate_trait_violation_pressure(self, trait_value: float,
envelope: StabilityEnvelope) -> float:
"""
Calculate violation pressure for individual trait.
Core Formula: VP = |actual - center| / (radius * compression_factor)
"""
# Calculate deviation from stability center
deviation = abs(trait_value - envelope.center)
# Normalize by stability envelope
normalized_radius = envelope.radius * envelope.compression_factor
# Calculate violation pressure
if normalized_radius > 0:
vp = deviation / normalized_radius
else:
vp = float('inf') if deviation > 0 else 0.0
# Clamp to reasonable range [0.0, 10.0]
return max(0.0, min(10.0, vp))
def _classify_violation_pressure(self, total_vp: float) -> ViolationClass:
"""Classify violation pressure into appropriate category"""
if total_vp < 0.25:
return ViolationClass.VP0_FULLY_LAWFUL
elif total_vp < 0.50:
return ViolationClass.VP1_STABLE_DRIFT
elif total_vp < 0.75:
return ViolationClass.VP2_INSTABILITY
elif total_vp < 1.00:
return ViolationClass.VP3_CRITICAL_DIVERGENCE
else:
return ViolationClass.VP4_COLLAPSE_THRESHOLD
def set_stability_envelope(self, trait_name: str, envelope: StabilityEnvelope):
"""Set custom stability envelope for specific trait"""
self.stability_envelopes[trait_name] = envelope
def get_stability_envelope(self, trait_name: str) -> Optional[StabilityEnvelope]:
"""Get stability envelope for specific trait"""
return self.stability_envelopes.get(trait_name)
def calculate_system_health_metrics(self) -> Dict[str, Any]:
"""Calculate system-wide health metrics based on VP history"""
if not self.vp_history:
return {"error": "No VP history available"}
recent_vp = [entry["total_vp"] for entry in self.vp_history[-100:]] # Last 100 entries
return {
"average_vp": sum(recent_vp) / len(recent_vp),
"max_vp": max(recent_vp),
"min_vp": min(recent_vp),
"vp_volatility": self._calculate_volatility(recent_vp),
"stability_trend": self._calculate_stability_trend(recent_vp),
"classification_distribution": self._get_classification_distribution(),
"total_measurements": len(self.vp_history)
}
def _calculate_volatility(self, vp_values: List[float]) -> float:
"""Calculate volatility of violation pressure values"""
if len(vp_values) < 2:
return 0.0
mean_vp = sum(vp_values) / len(vp_values)
variance = sum((x - mean_vp) ** 2 for x in vp_values) / len(vp_values)
return math.sqrt(variance)
def _calculate_stability_trend(self, vp_values: List[float]) -> str:
"""Calculate trend in violation pressure over time"""
if len(vp_values) < 10:
return "insufficient_data"
# Simple linear trend calculation
recent = vp_values[-10:]
early_avg = sum(recent[:5]) / 5
late_avg = sum(recent[5:]) / 5
if late_avg < early_avg * 0.9:
return "improving"
elif late_avg > early_avg * 1.1:
return "degrading"
else:
return "stable"
def _get_classification_distribution(self) -> Dict[str, int]:
"""Get distribution of VP classifications"""
distribution = {cls.value: 0 for cls in ViolationClass}
for entry in self.vp_history:
classification = entry["classification"]
distribution[classification] += 1
return distribution
def export_vp_analysis(self, trait_payload: Dict[str, float],
source_identity: Optional[str] = None) -> Dict[str, Any]:
"""Export comprehensive VP analysis"""
total_vp, breakdown = self.compute_violation_pressure(trait_payload, source_identity)
classification = self._classify_violation_pressure(total_vp)
return {
"analysis_timestamp": datetime.utcnow().isoformat() + "Z",
"source_identity": source_identity,
"total_violation_pressure": total_vp,
"classification": classification.value,
"per_trait_breakdown": breakdown,
"stability_envelopes": {
name: {
"center": env.center,
"radius": env.radius,
"compression_factor": env.compression_factor
}
for name, env in self.stability_envelopes.items()
if name in trait_payload
},
"system_health": self.calculate_system_health_metrics(),
"mathematical_formula": "VP_total = Σ(|actual - center| / (radius * compression))"
}
# Example usage and testing
if __name__ == "__main__":
# Initialize violation pressure monitor
from uuid_anchor_mechanism import EventPublisher
event_publisher = EventPublisher()
vp_monitor = ViolationMonitor(event_publisher)
# Test trait payload
test_payload = {
"intimacy": 0.8, # High intimacy
"commitment": 0.3, # Low commitment
"caregiving": 0.7, # Moderate caregiving
"violationpressure": 0.4, # Moderate VP
"reflectionindex": 0.9 # High reflection
}
print("=== Violation Pressure Calculation Test ===")
print(f"Test payload: {test_payload}")
# Calculate violation pressure
total_vp, breakdown = vp_monitor.compute_violation_pressure(test_payload, "test_identity_001")
print(f"Total Violation Pressure: {total_vp:.3f}")
print(f"Per-trait breakdown: {breakdown}")
# Export comprehensive analysis
analysis = vp_monitor.export_vp_analysis(test_payload, "test_identity_001")
print(f"VP Analysis: {analysis}")
# Show system health metrics
health_metrics = vp_monitor.calculate_system_health_metrics()
print(f"System Health: {health_metrics}")
print("=== Phase 0.2 Implementation Complete ===")
print("Violation Pressure Calculation operational and mathematically verified.")

Xet Storage Details

Size:
42.1 kB
·
Xet hash:
fed969469428b2f24fe77b71cb280a5b21dc2ebee6850b11e73cd28bbff76d04

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.