add entropy readings and plot

This commit is contained in:
2026-02-08 21:59:14 +01:00
parent 22ae12272e
commit adbaafa5ae
3 changed files with 43 additions and 9 deletions

View File

@@ -23,5 +23,5 @@ Problem :: struct {
}
Stats :: struct {
best, mean, worst: f64,
best, mean, worst, entropy: f64,
}

View File

@@ -26,22 +26,21 @@ run_ga :: proc(problem: Problem, survivor_selection: Survivor_Selection) {
for gen in 0 ..< GENERATIONS {
pop_fitnesses := evaluate_population(&population, problem.fitness_proc)
stats := compute_stats(pop_fitnesses[:], problem.maximize)
stats := compute_stats(&population, pop_fitnesses[:], problem.chromosome_size, problem.maximize)
append(&generation_stats, stats)
fmt.printfln(
"Gen %d: Best=%.4f Mean=%.4f Worst=%.4f",
"Gen %d: Best=%.4f Mean=%.4f Worst=%.4f Entropy=%.4f",
gen,
stats.best,
stats.mean,
stats.worst,
stats.entropy,
)
// Create offspring (no selection decisions here)
offspring := create_offspring_simple(&population)
offspring_fitnesses := evaluate_population(&offspring, problem.fitness_proc)
// Survivor selection decides who lives
next_gen := survivor_selection(
&population,
&offspring,
@@ -86,7 +85,35 @@ evaluate_population :: proc(
return fitnesses
}
compute_stats :: proc(fitnesses: []f64, maximize: bool) -> Stats {
compute_population_entropy :: proc(pop: ^Population, chromosome_size: int) -> f64 {
bit_counts := make([]int, chromosome_size, context.temp_allocator)
defer delete(bit_counts, context.temp_allocator)
// Count 1s at each bit position
for i in 0 ..< POPULATION_SIZE {
for j in 0 ..< chromosome_size {
if bit_array.get(pop[i], j) {
bit_counts[j] += 1
}
}
}
// Calculate entropy: H = -Σ p_i * log2(p_i)
entropy: f64 = 0.0
for count in bit_counts {
if count == 0 || count == POPULATION_SIZE {
continue // Skip if all 0s or all 1s (log(0) undefined)
}
p := f64(count) / f64(POPULATION_SIZE)
entropy -= p * math.log2(p)
}
return entropy
}
// Modified compute_stats to include entropy
compute_stats :: proc(pop: ^Population, fitnesses: []f64, chromosome_size: int, maximize: bool) -> Stats {
best := maximize ? -math.F64_MAX : math.F64_MAX
worst := maximize ? math.F64_MAX : -math.F64_MAX
sum := 0.0
@@ -102,7 +129,9 @@ compute_stats :: proc(fitnesses: []f64, maximize: bool) -> Stats {
sum += f
}
return {best, sum / f64(len(fitnesses)), worst}
entropy := compute_population_entropy(pop, chromosome_size)
return {best, sum / f64(len(fitnesses)), worst, entropy}
}
tournament_selection :: proc(pop: ^Population, fitnesses: []f64, maximize: bool) -> Chromosome {
@@ -360,7 +389,7 @@ write_results :: proc(filename: string, stats: []Stats) -> bool {
w: csv.Writer
csv.writer_init(&w, os.stream_from_handle(handle))
csv.write(&w, []string{"Generation", "Best", "Mean", "Worst"})
csv.write(&w, []string{"Generation", "Best", "Mean", "Worst", "Entropy"})
for stat, gen in stats {
csv.write(
@@ -370,6 +399,7 @@ write_results :: proc(filename: string, stats: []Stats) -> bool {
fmt.tprintf("%.6f", stat.best),
fmt.tprintf("%.6f", stat.mean),
fmt.tprintf("%.6f", stat.worst),
fmt.tprintf("%.6f", stat.entropy),
},
)
}

View File

@@ -3,9 +3,13 @@
↥0⋕↘1°csv
≡°⊂
°⍉≡⊟
⍜°⊟₃∩₃Line
⍜°⊟₃∩₃Line °⊂⌟
°⊸≡°◇°Data~Label {"best" "mean" "worst"}
Plot!(
°⊸XLabel "generations"
)
&fwa"output/plot.png"img"png"
°⊸Data~Label "entropy" Line
Plot!(°⊸XLabel "generations")
&fwa"output/entropy.png"img"png"