<commit_before>require 'metaheuristics/metaheuristic_interface'
require 'metaheuristics/search_results'

class AlpsGa < MetaheuristicInterface

	# From Hornby 2006 ALPS
	DEFAULTS = {
		:layer_size                  => 100,
		:max_layer_count             => 10,
		:tournament_size             => 7,
		:age_gap                     => 10,
		:aging_scheme                => :polynomial,
		:layer_elitism               => 3,
		:overall_elitism             => 0,
		:parents_from_previous_layer => true
	}

	attr_reader :results

	class Individual
		include Comparable

		attr_reader :age, :solution, :fitness

		# ctor
		def initialize(solution, age = 0)
			@solution = solution
			@age      = age
			@used     = false
			@fitness  = nil
		end

		# tag as used 
		def tag_as_used
			@used = true
		end

		# age
		def age!
			if @used
				@used = false
				@age += 1
			end
		end

		# evaluate, return fitness
		def evaluate(evaluator)
			@fitness = evaluator.call(solution)
		end

		# <=>
		def <=>(individual)
			self.fitness <=> individual.fitness
		end
	end # Individual

	# ctor
	def initialize(options)
		@layer_size                  = options[:layer_size                 ] || DEFAULTS[:layer_size                 ]
		@max_layer_count             = options[:max_layer_count            ] || DEFAULTS[:max_layer_count            ]
		@tournament_size             = options[:tournament_size            ] || DEFAULTS[:tournament_size            ]
		@age_gap                     = options[:age_gap                    ] || DEFAULTS[:age_gap                    ]
		@aging_scheme                = options[:aging_scheme               ] || DEFAULTS[:aging_scheme               ]
		@layer_elitism               = options[:layer_elitism              ] || DEFAULTS[:layer_elitism              ]
		@overall_elitism             = options[:overall_elitism            ] || DEFAULTS[:overall_elitism            ]
		@parents_from_previous_layer = options[:parents_from_previous_layer] || DEFAULTS[:parents_from_previous_layer]

		@evaluator       = options[:evaluator      ] || (raise ArgumentError)
		@mutation_rate   = options[:mutation_rate  ] || (raise ArgumentError)
		@genome_init     = options[:genome_init    ] || (raise ArgumentError)

		raise ArgumentError unless [:linear, :fibonacci, :polynomial, :exponential].include?(@aging_scheme)

		@results = SearchResults.new
		@layers  = []
	end

	# run once
	def run_once
		generation = @results.generation
		if generation == 0
			@layers[0] = Array.new(@layer_size){ Individual.new(@genome_init.call) }
			evaluate_layer(0)
		else
			# all but bottom layer
			(@layers.size - 1).downto(1) do |i|
				generate_new_population_layer(i)
				evaluate_layer(i)
			end

			# bottom layer
			if generation % @age_gap == 0
				move_to_layer(@layers[0], 1)
				@layers[0] = Array.new(@layer_size){ Individual.new(@genome_init.call) }
			else
				generate_new_population_layer(0)
			end
			evaluate_layer(0)
		end

		# age population
		@layers.each do |l|
			l.each do |individual|
				individual.age!
			end
		end

		# promote individuals
		promote_individuals

		@results.increment_generation

		@layers.each.with_index do |l, idx|
			l.sort!.reverse!
			$stderr << "= layer #{idx} : max age #{max_layer_age(idx)} ---------------------------------------------------------------\n"
			l.each do |i|
				$stderr << " - #{i.solution.__id__.to_s.rjust(7)}, fitness #{i.fitness.to_i.to_s.rjust(6)}, age #{i.age.to_s.rjust(4)}\n"
			end
		end
		$stderr << "\n"
	end<commit_msg>ALPS: display fitness as float<commit_after>require 'metaheuristic_interface'
require 'metaheuristics/search_results'

class AlpsGa < MetaheuristicInterface

	# From Hornby 2006 ALPS
	DEFAULTS = {
		:layer_size                  => 100,
		:max_layer_count             => 10,
		:tournament_size             => 7,
		:age_gap                     => 10,
		:aging_scheme                => :polynomial,
		:layer_elitism               => 3,
		:overall_elitism             => 0,
		:parents_from_previous_layer => true
	}

	attr_reader :results

	class Individual
		include Comparable

		attr_reader :age, :solution, :fitness

		# ctor
		def initialize(solution, age = 0)
			@solution = solution
			@age      = age
			@used     = false
			@fitness  = nil
		end

		# tag as used 
		def tag_as_used
			@used = true
		end

		# age
		def age!
			if @used
				@used = false
				@age += 1
			end
		end

		# evaluate, return fitness
		def evaluate(evaluator)
			@fitness = evaluator.call(solution)
		end

		# <=>
		def <=>(individual)
			self.fitness <=> individual.fitness
		end
	end # Individual

	# ctor
	def initialize(options)
		@layer_size                  = options[:layer_size                 ] || DEFAULTS[:layer_size                 ]
		@max_layer_count             = options[:max_layer_count            ] || DEFAULTS[:max_layer_count            ]
		@tournament_size             = options[:tournament_size            ] || DEFAULTS[:tournament_size            ]
		@age_gap                     = options[:age_gap                    ] || DEFAULTS[:age_gap                    ]
		@aging_scheme                = options[:aging_scheme               ] || DEFAULTS[:aging_scheme               ]
		@layer_elitism               = options[:layer_elitism              ] || DEFAULTS[:layer_elitism              ]
		@overall_elitism             = options[:overall_elitism            ] || DEFAULTS[:overall_elitism            ]
		@parents_from_previous_layer = options[:parents_from_previous_layer] || DEFAULTS[:parents_from_previous_layer]

		@evaluator       = options[:evaluator      ] || (raise ArgumentError)
		@mutation_rate   = options[:mutation_rate  ] || (raise ArgumentError)
		@genome_init     = options[:genome_init    ] || (raise ArgumentError)

		raise ArgumentError unless [:linear, :fibonacci, :polynomial, :exponential].include?(@aging_scheme)

		@results = SearchResults.new
		@layers  = []
	end

	# run once
	def run_once
		generation = @results.generation
		if generation == 0
			@layers[0] = Array.new(@layer_size){ Individual.new(@genome_init.call) }
			evaluate_layer(0)
		else
			# all but bottom layer
			(@layers.size - 1).downto(1) do |i|
				generate_new_population_layer(i)
				evaluate_layer(i)
			end

			# bottom layer
			if generation % @age_gap == 0
				move_to_layer(@layers[0], 1)
				@layers[0] = Array.new(@layer_size){ Individual.new(@genome_init.call) }
			else
				generate_new_population_layer(0)
			end
			evaluate_layer(0)
		end

		# age population
		@layers.each do |l|
			l.each do |individual|
				individual.age!
			end
		end

		# promote individuals
		promote_individuals

		@results.increment_generation

		@layers.each.with_index do |l, idx|
			l.sort!.reverse!
			$stderr << "= layer #{idx} : max age #{max_layer_age(idx)} ---------------------------------------------------------------\n"
			l.each do |i|
#				$stderr << " - #{i.solution.__id__.to_s.rjust(7)}, fitness #{i.fitness.to_i.to_s.rjust(6)}, age #{i.age.to_s.rjust(4)}\n"
				$stderr << " - #{i.solution.__id__.to_s.rjust(7)}, fitness #{('%.4f' % i.fitness).rjust(7)}, age #{i.age.to_s.rjust(4)}\n"
			end
		end
		$stderr << "\n"
	end
{'fraction_non_alphanumeric': 0.13072170738556585, 'fraction_numerical': 0.008986239820275204, 'mean_word_length': 2.2245359891353553, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2001131', 'n_tokens_mistral': 2677, 'n_tokens_neox': 2249, 'n_words': 446}