# quanta_sim_v3_multi_input.py import neat import numpy as np import os import logging import pickle import random import math import datetime import itertools # <-- YENİ: Girdi kombinasyonları için # --- Loglama Ayarları --- log_filename = f"quanta_log_v3_multi_input_{datetime.datetime.now():%Y%m%d_%H%M%S}.log" # <-- V3 için dosya adı logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s', handlers=[ logging.FileHandler(log_filename), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) logger.info("="*50) logger.info("Quanta Simülatörü Başlatılıyor (Sürüm 3 - Çoklu Girdi)") # <-- GÜNCELLENDİ logger.info("="*50) # --- Simülasyon Parametreleri --- NUM_TEST_VALUES_PER_AXIS = 3 # Her bir girdi ekseni için test edilecek değer sayısı (örn: 0.0, 0.5, 1.0) NUM_TRIALS_PER_INPUT_PAIR = 20 # Her bir girdi çifti için yapılacak deneme sayısı # Toplam test noktası sayısı = NUM_TEST_VALUES_PER_AXIS * NUM_TEST_VALUES_PER_AXIS # Toplam değerlendirme denemesi/Genom = NUM_TEST_POINTS * NUM_TRIALS_PER_INPUT_PAIR MAX_GENERATIONS = 150 # <-- Problem zorlaştığı için artırılabilir FITNESS_THRESHOLD = 0.97 # <-- Hedef zorlaştığı için eşik ayarlanabilir num_test_points = NUM_TEST_VALUES_PER_AXIS ** 2 # İki girdi olduğu için karesi total_trials_per_genome = num_test_points * NUM_TRIALS_PER_INPUT_PAIR logger.info(f"Eksen Başına Test Değeri Sayısı: {NUM_TEST_VALUES_PER_AXIS}") logger.info(f"Toplam Test Girdi Çifti Sayısı: {num_test_points}") logger.info(f"Girdi Çifti Başına Deneme Sayısı: {NUM_TRIALS_PER_INPUT_PAIR}") logger.info(f"Toplam Değerlendirme Denemesi/Genom: {total_trials_per_genome}") logger.info(f"Maksimum Nesil Sayısı: {MAX_GENERATIONS}") logger.info(f"Fitness Eşiği: {FITNESS_THRESHOLD}") # --- YENİ: İki Girdi İçin Hedef Olasılık Fonksiyonu --- def calculate_target_prob0(input1, input2): """ Verilen iki girdiye göre hedef P(0) olasılığını hesaplar. Lineer Fonksiyon: target_P0(x1, x2) = 0.1 + 0.5*x1 + 0.3*x2 """ # Değerlerin 0-1 aralığında kalmasını sağlayalım (isteğe bağlı, fonksiyona göre) target = 0.1 + 0.5 * input1 + 0.3 * input2 return max(0.0, min(1.0, target)) # Güvenlik önlemi logger.info(f"Hedef P(0) Fonksiyonu: P(0|x1, x2) = 0.1 + 0.5*x1 + 0.3*x2") # --- NEAT Fitness Fonksiyonu --- # (Bu fonksiyon Sürüm 3 için önemli ölçüde güncellendi) def eval_genomes(genomes, config): """ Popülasyondaki tüm genomların fitness değerlerini hesaplar. Fitness, ağın farklı girdi çiftleri için hedeflenen olasılıkları ne kadar iyi üretebildiğine göre belirlenir. """ # Test edilecek girdi değerlerini her eksen için belirleyelim axis_values = np.linspace(0.0, 1.0, NUM_TEST_VALUES_PER_AXIS) # Örnek: NUM_TEST_VALUES_PER_AXIS=3 ise -> [0.0, 0.5, 1.0] # Tüm girdi çiftlerini (kombinasyonlarını) oluşturalım # Örnek: [(0.0, 0.0), (0.0, 0.5), (0.0, 1.0), (0.5, 0.0), ..., (1.0, 1.0)] test_input_pairs = list(itertools.product(axis_values, repeat=2)) for genome_id, genome in genomes: genome.fitness = 0.0 # Başlangıç fitness'ı sıfırla try: # İki girdili config ile ağı oluştur net = neat.nn.FeedForwardNetwork.create(genome, config) except Exception as e: logger.error(f"Genome {genome_id} için ağ oluşturulamadı: {e}") genome.fitness = -10.0 continue total_squared_error = 0.0 # Her bir test girdi çifti için ağı değerlendir for input_pair in test_input_pairs: x1, x2 = input_pair # Girdi çiftini ayır target_prob_0 = calculate_target_prob0(x1, x2) # Bu çift için hedef P(0) count_0 = 0 # Her girdi çifti için N kez test et for _ in range(NUM_TRIALS_PER_INPUT_PAIR): try: # Ağı iki girdi ile aktive et output = net.activate(input_pair) # Ağın çıktısını yorumla (< 0.5 ise 0) if output[0] < 0.5: count_0 += 1 except Exception as e: logger.warning(f"Genome {genome_id}, Input {input_pair} ağ aktivasyonunda hata: {e}") pass # Gözlemlenen olasılığı hesapla if NUM_TRIALS_PER_INPUT_PAIR > 0: observed_prob_0 = count_0 / NUM_TRIALS_PER_INPUT_PAIR else: observed_prob_0 = 0.5 # Bu girdi çifti için hatanın karesini hesapla error = (observed_prob_0 - target_prob_0) ** 2 total_squared_error += error # Ortalama Karesel Hatayı (Mean Squared Error - MSE) hesapla average_squared_error = total_squared_error / len(test_input_pairs) # Fitness'ı hesapla (1 - RMSE) fitness = max(0.0, 1.0 - math.sqrt(average_squared_error)) genome.fitness = fitness # logger.info(f"Genome {genome_id}: AvgError^2 = {average_squared_error:.4f}, Fitness = {fitness:.4f}") # --- NEAT Çalıştırma Fonksiyonu --- def run_neat(config_file): """ NEAT evrimini başlatır ve yönetir. """ logger.info(f"NEAT yapılandırması yükleniyor: {config_file}") try: # V3 config dosyasını kullanarak config nesnesini oluştur config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_file) # <-- config_file şimdi v3 dosyasını gösteriyor config.fitness_threshold = FITNESS_THRESHOLD logger.info(f"Yapılandırma yüklendi. Giriş Sayısı: {config.genome_config.num_inputs}, Fitness Eşiği: {config.fitness_threshold}") except Exception as e: logger.critical(f"Yapılandırma dosyası yüklenemedi veya geçersiz: {config_file} - Hata: {e}") return None logger.info("Yeni popülasyon oluşturuluyor...") p = neat.Population(config) # Raporlayıcılar p.add_reporter(neat.StdOutReporter(True)) # Checkpoint checkpoint_prefix = 'neat-checkpoint-v3-' # <-- V3 için prefix p.add_reporter(neat.Checkpointer(15, filename_prefix=checkpoint_prefix)) # Daha seyrek kaydedilebilir logger.info(f"Checkpoint dosyaları '{checkpoint_prefix}*' olarak kaydedilecek.") logger.info(f"Evrim başlıyor (Maksimum {MAX_GENERATIONS} nesil)...") try: winner = p.run(eval_genomes, MAX_GENERATIONS) logger.info(' ' + "="*20 + " Evrim Tamamlandı " + "="*20) except Exception as e: logger.critical(f"Evrim sırasında kritik bir hata oluştu: {e}") return None # En iyi genomu işle if winner: logger.info(f'En iyi genom bulundu (Fitness: {winner.fitness:.6f}):') logger.info(f' {winner}') # En iyi genomu kaydet winner_filename = "winner_genome_v3.pkl" # <-- V3 için dosya adı try: with open(winner_filename, 'wb') as f: pickle.dump(winner, f) logger.info(f"En iyi genom '{winner_filename}' dosyasına başarıyla kaydedildi.") except Exception as e: logger.error(f"En iyi genom kaydedilemedi: {e}") # --- YENİ: En İyi Genomu Çoklu Girdi İle Detaylı Test Etme --- logger.info(" " + "="*20 + " En İyi Genom Detaylı Testi (Çoklu Girdi) " + "="*20) try: winner_net = neat.nn.FeedForwardNetwork.create(winner, config) test_trials_final = 500 # Final test için deneme sayısı logger.info(f"En iyi ağ, farklı girdi çiftleriyle {test_trials_final} kez test ediliyor...") # Daha hassas bir girdi ızgarası oluşturalım (örn: 5x5 = 25 nokta) final_axis_values = np.linspace(0.0, 1.0, 5) final_test_pairs = list(itertools.product(final_axis_values, repeat=2)) final_total_error_sq = 0.0 logger.info(f"{'Input (x1,x2)': <15} {'Target P(0)': <12} {'Observed P(0)': <14} {'Error^2': <10}") logger.info("-" * 60) for input_pair in final_test_pairs: x1, x2 = input_pair target_p0 = calculate_target_prob0(x1, x2) count_0 = 0 for _ in range(test_trials_final): output = winner_net.activate(input_pair) if output[0] < 0.5: count_0 += 1 observed_p0 = count_0 / test_trials_final error_sq = (observed_p0 - target_p0) ** 2 final_total_error_sq += error_sq logger.info(f"({x1:.2f}, {x2:.2f}) {target_p0:<12.3f} {observed_p0:<14.3f} {error_sq:<10.5f}") final_avg_error_sq = final_total_error_sq / len(final_test_pairs) final_rmse = math.sqrt(final_avg_error_sq) logger.info("-" * 60) logger.info(f"Final Ortalama Karesel Hata (MSE): {final_avg_error_sq:.6f}") logger.info(f"Final Kök Ortalama Karesel Hata (RMSE): {final_rmse:.6f}") logger.info(f"Final Fitness Yaklaşımı (1 - RMSE): {1.0 - final_rmse:.6f}") logger.info("-" * 60) except Exception as e: logger.error(f"En iyi genom test edilirken hata oluştu: {e}") else: logger.warning("Test edilecek bir kazanan genom bulunamadı.") logger.info("="*50) logger.info("Quanta Simülatörü Adım 3 (Çoklu Girdi) tamamlandı.") logger.info("="*50) return winner if __name__ == '__main__': # V3 için yeni yapılandırma dosyasının yolu local_dir = os.path.dirname(__file__) config_path = os.path.join(local_dir, 'config-feedforward-v3.txt') # <-- YENİ Config Dosyası if not os.path.exists(config_path): logger.critical(f"Yapılandırma dosyası bulunamadı: {config_path}") logger.critical("Lütfen 'config-feedforward-v3.txt' dosyasını Python betiğiyle aynı klasöre koyun.") else: # NEAT'i çalıştır run_neat(config_path)