""" Optimization Routines for Minimum Information Partition (MIP) in IIT This module implements sophisticated optimization algorithms for finding the Minimum Information Partition, which is crucial for computing integrated information Φ. Author: IIT Implementation Team Version: 1.0 """ import math import itertools import heapq import random from typing import Dict, List, Tuple, Optional, Any, Set, Callable from dataclasses import dataclass, field from collections import defaultdict import datetime from iit_core import ( SystemState, Transition, Concept, CauseEffectStructure, ProbabilityDistribution, TransitionProbabilityMatrix, Partition ) from phi_calculator import AdvancedPhiCalculator, PhiResult @dataclass class MIPOptimizationResult: """Result of MIP optimization with detailed metrics.""" best_partition: Optional[Partition] minimum_phi: float partitions_explored: int optimization_time: float convergence_criteria: str algorithm_used: str confidence_estimate: float search_space_coverage: float optimization_trace: List[Tuple[int, float]] = field(default_factory=list) @dataclass class PartitionHeuristic: """Heuristic for evaluating partitions without full computation.""" name: str evaluation_function: Callable[[Partition, Any], float] computational_cost: float class MIPOptimizer: """Advanced optimizer for finding Minimum Information Partitions.""" def __init__(self, phi_calculator: AdvancedPhiCalculator): self.phi_calculator = phi_calculator self.optimization_cache = {} self.heuristic_registry = self._initialize_heuristics() self.performance_stats = { 'total_optimizations': 0, 'cache_hits': 0, 'algorithm_usage': defaultdict(int), 'average_explored_partitions': 0 } def find_mip_exhaustive(self, mechanism: Set[int], purview: Set[int], system_state: SystemState) -> MIPOptimizationResult: """ Exhaustive search for MIP - guaranteed optimal but computationally expensive. Time Complexity: O(2^(|mechanism| + |purview|)) """ start_time = datetime.datetime.now() # Generate all possible bipartitions all_partitions = self._generate_all_bipartitions(mechanism, purview) best_partition = None minimum_phi = float('inf') optimization_trace = [] for i, partition in enumerate(all_partitions): # Evaluate partition phi_result = self._evaluate_partition_full(partition, mechanism, purview, system_state) current_phi = phi_result.phi_value optimization_trace.append((i, current_phi)) if current_phi < minimum_phi: minimum_phi = current_phi best_partition = partition optimization_time = (datetime.datetime.now() - start_time).total_seconds() search_coverage = 1.0 # Exhaustive explores all partitions confidence = 1.0 # Exact solution result = MIPOptimizationResult( best_partition=best_partition, minimum_phi=minimum_phi, partitions_explored=len(all_partitions), optimization_time=optimization_time, convergence_criteria="exhaustive_complete", algorithm_used="exhaustive", confidence_estimate=confidence, search_space_coverage=search_coverage, optimization_trace=optimization_trace ) self._update_stats("exhaustive", len(all_partitions)) return result def find_mip_branch_and_bound(self, mechanism: Set[int], purview: Set[int], system_state: SystemState, time_limit: float = 30.0) -> MIPOptimizationResult: """ Branch and bound search for MIP with pruning. Uses heuristics to prune search space while maintaining optimality guarantees. """ start_time = datetime.datetime.now() # Priority queue for exploring partitions (best candidates first) partition_queue = [] # Generate initial partitions with heuristic scores initial_partitions = self._generate_priority_partitions(mechanism, purview) for partition in initial_partitions: heuristic_score = self._evaluate_partition_heuristic( partition, mechanism, purview, system_state ) heapq.heappush(partition_queue, (heuristic_score, partition)) best_partition = None minimum_phi = float('inf') partitions_explored = 0 optimization_trace = [] while partition_queue and (datetime.datetime.now() - start_time).total_seconds() < time_limit: heuristic_score, partition = heapq.heappop(partition_queue) # Prune if heuristic bound exceeds current best if heuristic_score >= minimum_phi: continue # Full evaluation phi_result = self._evaluate_partition_full(partition, mechanism, purview, system_state) current_phi = phi_result.phi_value partitions_explored += 1 optimization_trace.append((partitions_explored, current_phi)) if current_phi < minimum_phi: minimum_phi = current_phi best_partition = partition optimization_time = (datetime.datetime.now() - start_time).total_seconds() # Estimate search coverage total_possible = self._count_total_partitions(mechanism, purview) search_coverage = min(1.0, partitions_explored / max(total_possible, 1)) confidence = 0.8 if search_coverage < 1.0 else 1.0 result = MIPOptimizationResult( best_partition=best_partition, minimum_phi=minimum_phi, partitions_explored=partitions_explored, optimization_time=optimization_time, convergence_criteria="branch_and_bound_complete" if search_coverage == 1.0 else "time_limit", algorithm_used="branch_and_bound", confidence_estimate=confidence, search_space_coverage=search_coverage, optimization_trace=optimization_trace ) self._update_stats("branch_and_bound", partitions_explored) return result def find_mip_genetic_algorithm(self, mechanism: Set[int], purview: Set[int], system_state: SystemState, population_size: int = 50, generations: int = 100, mutation_rate: float = 0.1) -> MIPOptimizationResult: """ Genetic algorithm for MIP search - probabilistic optimization. Good for large search spaces where exact methods are infeasible. """ start_time = datetime.datetime.now() # Initialize population with diverse partitions population = self._initialize_population(mechanism, purview, population_size) best_partition = None minimum_phi = float('inf') optimization_trace = [] for generation in range(generations): # Evaluate fitness (inverse of phi) fitness_scores = [] for partition in population: phi_result = self._evaluate_partition_full(partition, mechanism, purview, system_state) fitness = 1.0 / (phi_result.phi_value + 1e-10) # Avoid division by zero fitness_scores.append(fitness) if phi_result.phi_value < minimum_phi: minimum_phi = phi_result.phi_value best_partition = partition optimization_trace.append((generation, minimum_phi)) # Selection, crossover, and mutation population = self._evolve_population( population, fitness_scores, mechanism, purview, mutation_rate ) partitions_explored = generations * population_size optimization_time = (datetime.datetime.now() - start_time).total_seconds() # Estimate confidence based on convergence if len(optimization_trace) > 10: recent_variance = self._calculate_recent_variance(optimization_trace[-10:]) confidence = max(0.5, 1.0 - recent_variance) else: confidence = 0.7 result = MIPOptimizationResult( best_partition=best_partition, minimum_phi=minimum_phi, partitions_explored=partitions_explored, optimization_time=optimization_time, convergence_criteria=f"genetic_complete_{generations}_gens", algorithm_used="genetic_algorithm", confidence_estimate=confidence, search_space_coverage=min(1.0, partitions_explored / 1000.0), # Rough estimate optimization_trace=optimization_trace ) self._update_stats("genetic_algorithm", partitions_explored) return result def find_mip_simulated_annealing(self, mechanism: Set[int], purview: Set[int], system_state: SystemState, initial_temperature: float = 100.0, cooling_rate: float = 0.95, iterations: int = 1000) -> MIPOptimizationResult: """ Simulated annealing for MIP search - probabilistic hill climbing. Good for avoiding local minima in partition space. """ start_time = datetime.datetime.now() # Start with random partition current_partition = self._generate_random_partition(mechanism, purview) current_phi_result = self._evaluate_partition_full(current_partition, mechanism, purview, system_state) current_phi = current_phi_result.phi_value best_partition = current_partition minimum_phi = current_phi temperature = initial_temperature optimization_trace = [] for iteration in range(iterations): # Generate neighboring partition neighbor = self._generate_neighbor_partition(current_partition, mechanism, purview) neighbor_phi_result = self._evaluate_partition_full(neighbor, mechanism, purview, system_state) neighbor_phi = neighbor_phi_result.phi_value # Accept or reject neighbor delta_phi = neighbor_phi - current_phi accept_probability = math.exp(-delta_phi / max(temperature, 0.001)) if delta_phi < 0 or random.random() < accept_probability: current_partition = neighbor current_phi = neighbor_phi if neighbor_phi < minimum_phi: minimum_phi = neighbor_phi best_partition = neighbor optimization_trace.append((iteration, minimum_phi)) # Cool down temperature *= cooling_rate partitions_explored = iterations optimization_time = (datetime.datetime.now() - start_time).total_seconds() # Confidence based on convergence pattern if len(optimization_trace) > 20: recent_improvement = optimization_trace[-20][0] - optimization_trace[-1][0] confidence = max(0.6, 1.0 - (recent_improvement / iterations)) else: confidence = 0.7 result = MIPOptimizationResult( best_partition=best_partition, minimum_phi=minimum_phi, partitions_explored=partitions_explored, optimization_time=optimization_time, convergence_criteria=f"annealing_complete_{iterations}_iter", algorithm_used="simulated_annealing", confidence_estimate=confidence, search_space_coverage=min(1.0, iterations / 500.0), # Rough estimate optimization_trace=optimization_trace ) self._update_stats("simulated_annealing", partitions_explored) return result def find_mip_adaptive_hybrid(self, mechanism: Set[int], purview: Set[int], system_state: SystemState, time_budget: float = 60.0) -> MIPOptimizationResult: """ Adaptive hybrid algorithm that switches between methods based on performance. Automatically selects the most promising algorithm for the current problem. """ start_time = datetime.datetime.now() # Quick initial estimate using heuristics initial_partitions = self._generate_priority_partitions(mechanism, purview)[:5] best_partition = None minimum_phi = float('inf') for partition in initial_partitions: phi_result = self._evaluate_partition_full(partition, mechanism, purview, system_state) if phi_result.phi_value < minimum_phi: minimum_phi = phi_result.phi_value best_partition = partition # Decide on primary algorithm based on problem size problem_size = len(mechanism) + len(purview) if problem_size <= 6: # Small problem: use branch and bound return self.find_mip_branch_and_bound(mechanism, purview, system_state, time_budget) elif problem_size <= 10: # Medium problem: try simulated annealing first sa_result = self.find_mip_simulated_annealing( mechanism, purview, system_state, iterations=min(500, int(time_budget * 10)) ) if (datetime.datetime.now() - start_time).total_seconds() < time_budget * 0.5: # Try genetic algorithm if time permits ga_result = self.find_mip_genetic_algorithm( mechanism, purview, system_state, population_size=30, generations=min(50, int(time_budget * 5)) ) # Return better result if ga_result.minimum_phi < sa_result.minimum_phi: return ga_result return sa_result else: # Large problem: genetic algorithm with parallel evaluation return self.find_mip_genetic_algorithm( mechanism, purview, system_state, population_size=min(100, int(time_budget * 2)), generations=min(200, int(time_budget * 10)) ) def _generate_all_bipartitions(self, mechanism: Set[int], purview: Set[int]) -> List[Partition]: """Generate all possible bipartitions of mechanism and purview.""" partitions = [] # Generate mechanism bipartitions mechanism_partitions = self._bipartition_set(mechanism) purview_partitions = self._bipartition_set(purview) # Combine all possible bipartitions for mech_part in mechanism_partitions: for pur_part in purview_partitions: partitions.append(Partition( mech_part[0], mech_part[1], pur_part[0], pur_part[1] )) # Add single-sided partitions if len(mechanism) > 1: for mech_part in mechanism_partitions: partitions.append(Partition( mech_part[0], mech_part[1], purview, set() )) if len(purview) > 1: for pur_part in purview_partitions: partitions.append(Partition( mechanism, set(), pur_part[0], pur_part[1] )) return partitions def _bipartition_set(self, element_set: Set[int]) -> List[Tuple[Set[int], Set[int]]]: """Generate all bipartitions of a set.""" if len(element_set) < 2: return [] bipartitions = [] element_list = list(element_set) # Generate all non-trivial bipartitions for k in range(1, len(element_list) // 2 + 1): for subset in itertools.combinations(element_list, k): part1 = set(subset) part2 = element_set - part1 bipartitions.append((part1, part2)) return bipartitions def _generate_priority_partitions(self, mechanism: Set[int], purview: Set[int]) -> List[Partition]: """Generate priority partitions for initial search.""" priority_partitions = [] # Balanced splits first if len(mechanism) > 1: mech_list = list(mechanism) mid = len(mech_list) // 2 priority_partitions.append(Partition( set(mech_list[:mid]), set(mech_list[mid:]), purview, set() )) if len(purview) > 1: pur_list = list(purview) mid = len(pur_list) // 2 priority_partitions.append(Partition( mechanism, set(), set(pur_list[:mid]), set(pur_list[mid:]) )) # Single-element partitions for elem in mechanism: priority_partitions.append(Partition( {elem}, mechanism - {elem}, purview, set() )) for elem in purview: priority_partitions.append(Partition( mechanism, set(), {elem}, purview - {elem} )) return priority_partitions def _evaluate_partition_full(self, partition: Partition, mechanism: Set[int], purview: Set[int], system_state: SystemState) -> PhiResult: """Full evaluation of partition using phi calculator.""" return self.phi_calculator.compute_phi_exhaustive(mechanism, purview, system_state) def _evaluate_partition_heuristic(self, partition: Partition, mechanism: Set[int], purview: Set[int], system_state: SystemState) -> float: """Quick heuristic evaluation of partition quality.""" # Simple heuristic: prefer balanced partitions mech_balance = abs(len(partition.mechanism_1) - len(partition.mechanism_2)) pur_balance = abs(len(partition.purview_1) - len(partition.purview_2)) # Lower balance score = better heuristic balance_score = mech_balance + pur_balance # Add size penalty for unbalanced partitions size_penalty = max(len(partition.mechanism_1), len(partition.mechanism_2)) + \ max(len(partition.purview_1), len(partition.purview_2)) return balance_score + size_penalty * 0.1 def _initialize_population(self, mechanism: Set[int], purview: Set[int], population_size: int) -> List[Partition]: """Initialize diverse population for genetic algorithm.""" population = [] # Add some known good partitions priority_partitions = self._generate_priority_partitions(mechanism, purview) population.extend(priority_partitions[:population_size // 3]) # Fill rest with random partitions while len(population) < population_size: random_partition = self._generate_random_partition(mechanism, purview) population.append(random_partition) return population def _generate_random_partition(self, mechanism: Set[int], purview: Set[int]) -> Partition: """Generate random bipartition.""" # Random mechanism split mech_list = list(mechanism) random.shuffle(mech_list) if len(mech_list) > 1: split_point = random.randint(1, len(mech_list) - 1) mech1 = set(mech_list[:split_point]) mech2 = set(mech_list[split_point:]) else: mech1 = set(mechanism) mech2 = set() # Random purview split pur_list = list(purview) random.shuffle(pur_list) if len(pur_list) > 1: split_point = random.randint(1, len(pur_list) - 1) pur1 = set(pur_list[:split_point]) pur2 = set(pur_list[split_point:]) else: pur1 = set(purview) pur2 = set() return Partition(mech1, mech2, pur1, pur2) def _evolve_population(self, population: List[Partition], fitness_scores: List[float], mechanism: Set[int], purview: Set[int], mutation_rate: float) -> List[Partition]: """Evolve population using selection, crossover, and mutation.""" # Selection (tournament selection) selected = [] for _ in range(len(population)): tournament = random.sample(list(zip(population, fitness_scores)), 3) winner = max(tournament, key=lambda x: x[1]) selected.append(winner[0]) # Crossover and mutation new_population = [] for i in range(0, len(selected), 2): if i + 1 < len(selected): parent1, parent2 = selected[i], selected[i + 1] # Crossover child1, child2 = self._crossover_partitions(parent1, parent2, mechanism, purview) # Mutation if random.random() < mutation_rate: child1 = self._mutate_partition(child1, mechanism, purview) if random.random() < mutation_rate: child2 = self._mutate_partition(child2, mechanism, purview) new_population.extend([child1, child2]) # Ensure population size while len(new_population) < len(population): new_population.append(self._generate_random_partition(mechanism, purview)) return new_population[:len(population)] def _crossover_partitions(self, parent1: Partition, parent2: Partition, mechanism: Set[int], purview: Set[int]) -> Tuple[Partition, Partition]: """Crossover operation for genetic algorithm.""" # Simple crossover: mix mechanism splits all_mech_elements = list(mechanism) random.shuffle(all_mech_elements) split_point = random.randint(0, len(all_mech_elements)) child1_mech1 = set(all_mech_elements[:split_point]) child1_mech2 = mechanism - child1_mech1 # For child2, use different split random.shuffle(all_mech_elements) split_point = random.randint(0, len(all_mech_elements)) child2_mech1 = set(all_mech_elements[:split_point]) child2_mech2 = mechanism - child2_mech1 # Purview from parents child1 = Partition(child1_mech1, child1_mech2, parent1.purview_1, parent1.purview_2) child2 = Partition(child2_mech1, child2_mech2, parent2.purview_1, parent2.purview_2) return child1, child2 def _mutate_partition(self, partition: Partition, mechanism: Set[int], purview: Set[int]) -> Partition: """Mutation operation for genetic algorithm.""" # Randomly move one element if random.random() < 0.5 and len(partition.mechanism_1) > 0: # Move from mechanism_1 to mechanism_2 element = random.choice(list(partition.mechanism_1)) new_mech1 = partition.mechanism_1 - {element} new_mech2 = partition.mechanism_2 | {element} return Partition(new_mech1, new_mech2, partition.purview_1, partition.purview_2) elif len(partition.mechanism_2) > 0: # Move from mechanism_2 to mechanism_1 element = random.choice(list(partition.mechanism_2)) new_mech1 = partition.mechanism_1 | {element} new_mech2 = partition.mechanism_2 - {element} return Partition(new_mech1, new_mech2, partition.purview_1, partition.purview_2) return partition def _generate_neighbor_partition(self, current: Partition, mechanism: Set[int], purview: Set[int]) -> Partition: """Generate neighboring partition for simulated annealing.""" return self._mutate_partition(current, mechanism, purview) def _count_total_partitions(self, mechanism: Set[int], purview: Set[int]) -> int: """Count total possible partitions.""" # Simplified count return (2 ** len(mechanism) - 2) * (2 ** len(purview) - 2) + 1 def _calculate_recent_variance(self, trace: List[Tuple[int, float]]) -> float: """Calculate variance in recent optimization trace.""" if len(trace) < 2: return 0.0 values = [value for _, value in trace] mean = sum(values) / len(values) variance = sum((x - mean) ** 2 for x in values) / len(values) return variance / (mean + 1e-10) # Normalized variance def _initialize_heuristics(self) -> Dict[str, PartitionHeuristic]: """Initialize heuristic registry.""" heuristics = {} heuristics['balance'] = PartitionHeuristic( name='balance', evaluation_function=self._balance_heuristic, computational_cost=1.0 ) heuristics['size'] = PartitionHeuristic( name='size', evaluation_function=self._size_heuristic, computational_cost=1.0 ) return heuristics def _balance_heuristic(self, partition: Partition, context: Any) -> float: """Balance heuristic for partition evaluation.""" mech_balance = abs(len(partition.mechanism_1) - len(partition.mechanism_2)) pur_balance = abs(len(partition.purview_1) - len(partition.purview_2)) return mech_balance + pur_balance def _size_heuristic(self, partition: Partition, context: Any) -> float: """Size heuristic for partition evaluation.""" total_size = (len(partition.mechanism_1) + len(partition.mechanism_2) + len(partition.purview_1) + len(partition.purview_2)) return total_size def _update_stats(self, algorithm: str, partitions_explored: int): """Update performance statistics.""" self.performance_stats['total_optimizations'] += 1 self.performance_stats['algorithm_usage'][algorithm] += 1 # Update average explored partitions current_avg = self.performance_stats['average_explored_partitions'] total_opt = self.performance_stats['total_optimizations'] new_avg = (current_avg * (total_opt - 1) + partitions_explored) / total_opt self.performance_stats['average_explored_partitions'] = new_avg def get_performance_stats(self) -> Dict[str, Any]: """Get performance statistics.""" return self.performance_stats.copy() def reset_stats(self): """Reset performance statistics.""" self.performance_stats = { 'total_optimizations': 0, 'cache_hits': 0, 'algorithm_usage': defaultdict(int), 'average_explored_partitions': 0 } class MIPBenchmark: """Benchmark suite for MIP optimization algorithms.""" def __init__(self, phi_calculator: AdvancedPhiCalculator): self.phi_calculator = phi_calculator self.optimizer = MIPOptimizer(phi_calculator) def benchmark_algorithms(self, test_cases: List[Tuple[Set[int], Set[int], SystemState]], algorithms: Optional[List[str]] = None) -> Dict[str, Any]: """Benchmark different MIP algorithms on test cases.""" if algorithms is None: algorithms = ['exhaustive', 'branch_and_bound', 'genetic_algorithm', 'simulated_annealing'] results = {} for algorithm in algorithms: algorithm_results = { 'total_time': 0.0, 'avg_partitions_explored': 0.0, 'avg_phi_found': 0.0, 'success_rate': 0.0, 'test_results': [] } for mechanism, purview, system_state in test_cases: try: if algorithm == 'exhaustive': result = self.optimizer.find_mip_exhaustive(mechanism, purview, system_state) elif algorithm == 'branch_and_bound': result = self.optimizer.find_mip_branch_and_bound(mechanism, purview, system_state) elif algorithm == 'genetic_algorithm': result = self.optimizer.find_mip_genetic_algorithm(mechanism, purview, system_state) elif algorithm == 'simulated_annealing': result = self.optimizer.find_mip_simulated_annealing(mechanism, purview, system_state) algorithm_results['test_results'].append(result) algorithm_results['total_time'] += result.optimization_time algorithm_results['avg_partitions_explored'] += result.partitions_explored algorithm_results['avg_phi_found'] += result.minimum_phi except Exception as e: print(f"Algorithm {algorithm} failed on test case: {e}") algorithm_results['success_rate'] -= 1.0 / len(test_cases) # Calculate averages if algorithm_results['test_results']: n_tests = len(algorithm_results['test_results']) algorithm_results['avg_partitions_explored'] /= n_tests algorithm_results['avg_phi_found'] /= n_tests algorithm_results['success_rate'] += 1.0 # Reset to 0, then add successes algorithm_results['success_rate'] = len(algorithm_results['test_results']) / n_tests results[algorithm] = algorithm_results return results def generate_test_cases(self, num_cases: int = 10) -> List[Tuple[Set[int], Set[int], SystemState]]: """Generate diverse test cases for benchmarking.""" test_cases = [] for case_id in range(num_cases): # Random system size num_elements = random.randint(2, 5) elements = set(range(num_elements)) # Random mechanism and purview mechanism_size = random.randint(1, num_elements) purview_size = random.randint(1, num_elements) mechanism = set(random.sample(list(elements), mechanism_size)) purview = set(random.sample(list(elements), purview_size)) # Random system state state_elements = tuple(random.randint(0, 1) for _ in range(num_elements)) system_state = SystemState(state_elements, 1.0) test_cases.append((mechanism, purview, system_state)) return test_cases def setup_test_transitions(tpm: TransitionProbabilityMatrix, num_elements: int): """Setup test transitions for MIP optimization.""" states = [] for bits in itertools.product([0, 1], repeat=num_elements): states.append(SystemState(bits, 1.0 / (2 ** num_elements))) # Add deterministic and stochastic transitions for i, from_state in enumerate(states): # Primary transition to_state = states[(i + 1) % len(states)] tpm.add_transition(from_state, to_state, 0.7) # Secondary transitions for j, other_state in enumerate(states): if i != j and j % 2 == 0: tpm.add_transition(from_state, other_state, 0.3 / (len(states) - 1)) if __name__ == "__main__": # Example usage and testing print("MIP Optimization - Example Usage") print("=" * 40) # Create test system from iit_core import IITCalculator from phi_calculator import AdvancedPhiCalculator tpm = TransitionProbabilityMatrix(3) calculator = IITCalculator(3) phi_calc = AdvancedPhiCalculator(tpm, 3) setup_test_transitions(tpm, 3) optimizer = MIPOptimizer(phi_calc) # Test MIP optimization mechanism = {0, 1, 2} purview = {0, 1, 2} system_state = SystemState((1, 0, 1), 1.0) print("Testing MIP Optimization Algorithms:") # Exhaustive search (for small problems) print("\n1. Exhaustive Search:") result = optimizer.find_mip_exhaustive(mechanism, purview, system_state) print(f" Minimum Φ: {result.minimum_phi:.6f}") print(f" Partitions explored: {result.partitions_explored}") print(f" Time: {result.optimization_time:.4f}s") print(f" Confidence: {result.confidence_estimate:.2f}") # Branch and bound print("\n2. Branch and Bound:") result = optimizer.find_mip_branch_and_bound(mechanism, purview, system_state, time_limit=5.0) print(f" Minimum Φ: {result.minimum_phi:.6f}") print(f" Partitions explored: {result.partitions_explored}") print(f" Time: {result.optimization_time:.4f}s") print(f" Search coverage: {result.search_space_coverage:.2%}") # Genetic algorithm print("\n3. Genetic Algorithm:") result = optimizer.find_mip_genetic_algorithm(mechanism, purview, system_state, population_size=20, generations=50) print(f" Minimum Φ: {result.minimum_phi:.6f}") print(f" Partitions explored: {result.partitions_explored}") print(f" Time: {result.optimization_time:.4f}s") print(f" Confidence: {result.confidence_estimate:.2f}") # Simulated annealing print("\n4. Simulated Annealing:") result = optimizer.find_mip_simulated_annealing(mechanism, purview, system_state, iterations=200) print(f" Minimum Φ: {result.minimum_phi:.6f}") print(f" Partitions explored: {result.partitions_explored}") print(f" Time: {result.optimization_time:.4f}s") print(f" Confidence: {result.confidence_estimate:.2f}") # Adaptive hybrid print("\n5. Adaptive Hybrid:") result = optimizer.find_mip_adaptive_hybrid(mechanism, purview, system_state, time_budget=10.0) print(f" Minimum Φ: {result.minimum_phi:.6f}") print(f" Algorithm used: {result.algorithm_used}") print(f" Time: {result.optimization_time:.4f}s") print(f" Confidence: {result.confidence_estimate:.2f}") # Performance statistics print("\nPerformance Statistics:") stats = optimizer.get_performance_stats() for key, value in stats.items(): print(f" {key}: {value}") # Benchmark print("\nRunning Benchmark...") benchmark = MIPBenchmark(phi_calc) test_cases = benchmark.generate_test_cases(5) benchmark_results = benchmark.benchmark_algorithms(test_cases, ['branch_and_bound', 'genetic_algorithm']) print("Benchmark Results:") for algorithm, results in benchmark_results.items(): print(f"\n{algorithm}:") print(f" Average time: {results['total_time'] / len(test_cases):.4f}s") print(f" Average partitions explored: {results['avg_partitions_explored']:.1f}") print(f" Success rate: {results['success_rate']:.2%}") print("\nMIP optimization initialized successfully!") def setup_test_transitions(tpm: TransitionProbabilityMatrix, num_elements: int): """Setup test transitions for MIP optimization.""" states = [] for bits in itertools.product([0, 1], repeat=num_elements): states.append(SystemState(bits, 1.0 / (2 ** num_elements))) # Add deterministic and stochastic transitions for i, from_state in enumerate(states): # Primary transition to_state = states[(i + 1) % len(states)] tpm.add_transition(from_state, to_state, 0.7) # Secondary transitions for j, other_state in enumerate(states): if i != j and j % 2 == 0: tpm.add_transition(from_state, other_state, 0.3 / (len(states) - 1))