All checks were successful
		
		
	
	Check usage of free licenses / check-licenses (pull_request) Successful in 1m38s
				
			Add copyright notice / copyright_notice (pull_request) Successful in 3m16s
				
			Run unit tests / unit_tests (pull_request) Successful in 5m31s
				
			Build Nginx-based docker image / build-static-assets (pull_request) Successful in 41m31s
				
			
		
			
				
	
	
		
			62 lines
		
	
	
		
			1.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			62 lines
		
	
	
		
			1.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # Copyright (C) 2024-2025 LibreWeddingPlanner contributors
 | |
| 
 | |
| # frozen_string_literal: true
 | |
| 
 | |
| module VNS
 | |
|   class Engine
 | |
|     class << self
 | |
|       def sequence(elements)
 | |
|         elements = elements.to_a
 | |
|         (elements + elements.reverse).chunk(&:itself).map(&:first)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def target_function(&function)
 | |
|       @target_function = function
 | |
|     end
 | |
| 
 | |
|     def add_perturbation(klass)
 | |
|       @perturbations ||= Set.new
 | |
|       @perturbations << klass
 | |
|     end
 | |
| 
 | |
|     attr_writer :initial_solution
 | |
| 
 | |
|     def run
 | |
|       raise 'No target function defined' unless @target_function
 | |
|       raise 'No perturbations defined' unless @perturbations
 | |
|       raise 'No initial solution defined' unless @initial_solution
 | |
| 
 | |
|       @best_solution = @initial_solution
 | |
|       @best_score = @target_function.call(@best_solution)
 | |
| 
 | |
|       self.class.sequence(@perturbations).each do |perturbation|
 | |
|         optimize(perturbation)
 | |
|       end
 | |
| 
 | |
|       @best_solution
 | |
|     end
 | |
| 
 | |
|     private
 | |
| 
 | |
|     def optimize(perturbation_klass)
 | |
|       loop do
 | |
|         optimized = false
 | |
| 
 | |
|         perturbation_klass.new(@best_solution).each do |alternative_solution|
 | |
|           score = @target_function.call(alternative_solution)
 | |
|           next if score >= @best_score
 | |
| 
 | |
|           @best_solution = alternative_solution.deep_dup
 | |
|           @best_score = score
 | |
|           optimized = true
 | |
| 
 | |
|           break
 | |
|         end
 | |
| 
 | |
|         return unless optimized
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |