# quanta_sim_v2_input_dep.py import neat import numpy as np import os import logging import pickle import random import math import datetime # Görselleştirme importları YOKTUR. # --- Loglama Ayarları --- log_filename = f"quanta_log_v2_input_dep_{datetime.datetime.now():%Y%m%d_%H%M%S}.log" # <-- V2 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 2 - Girdiye Bağlı Olasılık)") # <-- GÜNCELLENDİ logger.info("="*50) # --- Simülasyon Parametreleri --- # TARGET_PROB_0 artık sabit değil, girdiye göre hesaplanacak. # Bu yüzden aşağıdaki satırları kaldırabilir veya yorum satırı yapabiliriz. # TARGET_PROB_0 = 0.7 # TARGET_PROB_1 = 1.0 - TARGET_PROB_0 NUM_TEST_INPUTS = 6 # Farklı girdi değerlerini test etmek için kullanılacak girdi sayısı NUM_TRIALS_PER_INPUT = 20 # Her bir girdi değeri için yapılacak deneme sayısı # Toplam deneme sayısı = NUM_TEST_INPUTS * NUM_TRIALS_PER_INPUT (örn: 6 * 20 = 120) MAX_GENERATIONS = 100 # <-- Gerekirse artırılabilir, problem biraz daha zorlaştı. FITNESS_THRESHOLD = 0.98 # <-- Hedef biraz daha zor olduğu için eşiği hafifçe düşürebiliriz (isteğe bağlı) logger.info(f"Test Edilecek Girdi Sayısı: {NUM_TEST_INPUTS}") logger.info(f"Girdi Başına Deneme Sayısı: {NUM_TRIALS_PER_INPUT}") logger.info(f"Toplam Değerlendirme Denemesi/Genom: {NUM_TEST_INPUTS * NUM_TRIALS_PER_INPUT}") logger.info(f"Maksimum Nesil Sayısı: {MAX_GENERATIONS}") logger.info(f"Fitness Eşiği: {FITNESS_THRESHOLD}") # --- YENİ: Hedef Olasılık Fonksiyonu --- def calculate_target_prob0(input_value): """ Verilen girdiye göre hedef P(0) olasılığını hesaplar. Lineer Fonksiyon: target_P0(x) = 0.1 + 0.8 * x """ return 0.1 + 0.8 * input_value logger.info(f"Hedef P(0) Fonksiyonu: P(0|x) = 0.1 + 0.8 * x") # --- NEAT Fitness Fonksiyonu --- # (Bu fonksiyon Sürüm 2 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ı girdiler için hedeflenen olasılıkları ne kadar iyi üretebildiğine göre belirlenir. """ # Test edilecek girdi değerlerini belirleyelim (0 ile 1 arasında eşit aralıklı) test_inputs = np.linspace(0.0, 1.0, NUM_TEST_INPUTS) # Örnek: NUM_TEST_INPUTS=6 ise -> [0.0, 0.2, 0.4, 0.6, 0.8, 1.0] for genome_id, genome in genomes: genome.fitness = 0.0 # Başlangıç fitness'ı sıfırla try: 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 # Daha büyük ceza verilebilir continue total_squared_error = 0.0 # Her bir test girdisi için ağı değerlendir for net_input_val in test_inputs: net_input = (net_input_val,) # Girdiyi tuple formatına getir target_prob_0 = calculate_target_prob0(net_input_val) # Bu girdi için hedef P(0) count_0 = 0 # Her girdi için N kez test et for _ in range(NUM_TRIALS_PER_INPUT): try: output = net.activate(net_input) # 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 {net_input_val:.2f} ağ aktivasyonunda hata: {e}") # Hata durumunda bu denemeyi atla veya penaltı ver pass # Şimdilik sadece atlıyoruz # Gözlemlenen olasılığı hesapla if NUM_TRIALS_PER_INPUT > 0: observed_prob_0 = count_0 / NUM_TRIALS_PER_INPUT else: observed_prob_0 = 0.5 # Sıfıra bölme hatasını önle (pek olası değil ama güvenli) # Bu girdi için hatanın karesini hesapla error = (observed_prob_0 - target_prob_0) ** 2 total_squared_error += error # logger.debug(f" Genome {genome_id}, Input {net_input_val:.2f}: TargetP0={target_prob_0:.3f}, ObsP0={observed_prob_0:.3f}, Error^2={error:.4f}") # Ortalama Karesel Hatayı (Mean Squared Error - MSE) hesapla average_squared_error = total_squared_error / NUM_TEST_INPUTS # Fitness'ı hesapla: Hata ne kadar küçükse, fitness o kadar yüksek (Maks 1.0) # Hatanın karekökünü (RMSE gibi) 1'den çıkaralım 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: config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_file) # Fitness eşiğini config dosyasından değil, koddan alalım config.fitness_threshold = FITNESS_THRESHOLD # <-- V2 için güncel eşik logger.info(f"Yapılandırma yüklendi. 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) # İstatistikleri ve loglamayı ayarlama p.add_reporter(neat.StdOutReporter(True)) # Konsola raporlama # stats = neat.StatisticsReporter() # Görselleştirme YOK, bu yüzden buna gerek yok # p.add_reporter(stats) # Checkpoint (yedek) alma checkpoint_prefix = 'neat-checkpoint-v2-' # <-- V2 için farklı prefix p.add_reporter(neat.Checkpointer(10, filename_prefix=checkpoint_prefix)) 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 göster/kaydet if winner: logger.info(f'En iyi genom bulundu (Fitness: {winner.fitness:.6f}):') logger.info(f' {winner}') # Konsola detaylı genom bilgisini yazdırır # En iyi genomu dosyaya kaydet (pickle ile) winner_filename = "winner_genome_v2.pkl" # <-- V2 için farklı 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 Farklı Girdilerle Test Etme --- logger.info(" " + "="*20 + " En İyi Genom Detaylı Testi " + "="*20) try: winner_net = neat.nn.FeedForwardNetwork.create(winner, config) test_trials_final = 1000 # Her girdi için final test deneme sayısı logger.info(f"En iyi ağ, farklı girdilerle {test_trials_final} kez test ediliyor...") test_inputs_final = np.linspace(0.0, 1.0, 11) # Daha fazla ara noktayla test edelim (0.0, 0.1, ..., 1.0) final_total_error_sq = 0.0 results = [] for net_input_val in test_inputs_final: target_p0 = calculate_target_prob0(net_input_val) net_input = (net_input_val,) count_0 = 0 for _ in range(test_trials_final): output = winner_net.activate(net_input) 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 results.append({ "input": net_input_val, "target_p0": target_p0, "observed_p0": observed_p0, "error_sq": error_sq }) logger.info(f" Input={net_input_val: <4.2f} -> TargetP(0)={target_p0: <5.3f}, ObservedP(0)={observed_p0: <5.3f}, Error^2={error_sq:.5f}") final_avg_error_sq = final_total_error_sq / len(test_inputs_final) final_rmse = math.sqrt(final_avg_error_sq) logger.info("-" * 40) 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("-" * 40) 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 2 (Girdiye Bağlı Olasılık) tamamlandı.") logger.info("="*50) return winner if __name__ == '__main__': # Yapılandırma dosyasının yolu (Python betiği ile aynı klasörde olduğunu varsayar) local_dir = os.path.dirname(__file__) # V1'deki config dosyasını kullanıyoruz, ismi aynı kalabilir. config_path = os.path.join(local_dir, 'config-feedforward.txt') if not os.path.exists(config_path): logger.critical(f"Yapılandırma dosyası bulunamadı: {config_path}") logger.critical("Lütfen 'config-feedforward.txt' dosyasını Python betiğiyle aynı klasöre koyun.") else: # NEAT'i çalıştır run_neat(config_path)