Coverage for moptipy / algorithms / mo / morls.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-24 08:49 +0000

1""" 

2A multi-objective version of the Randomized Local Search algorithm. 

3 

4The original randomized local search (RLS) always remembers the best-so-far 

5solution and, in each step, generates a new one by applying the unary search 

6operator. If the new solution is not worse than the best-so-far one (according 

7to the single objective function), it becomes the new best-so-far solution. 

8In this multi-objective version, we accept the new solution if it is not 

9dominated by the best-so-far solution. 

10""" 

11from typing import Callable, Final 

12 

13from numpy.random import Generator 

14 

15from moptipy.api.algorithm import Algorithm1 

16from moptipy.api.mo_algorithm import MOAlgorithm 

17from moptipy.api.mo_process import MOProcess 

18from moptipy.api.operators import Op0, Op1 

19 

20 

21class MORLS(Algorithm1, MOAlgorithm): 

22 """The MO-RLS is a local search accepting all non-worsening moves.""" 

23 

24 def solve_mo(self, process: MOProcess) -> None: 

25 """ 

26 Apply the MO-RLS to an optimization problem. 

27 

28 :param process: the black-box process object 

29 """ 

30 # Create records for old and new point in the search space. 

31 best_x = process.create() # record for best-so-far solution 

32 best_f = process.f_create() # the objective values 

33 new_x = process.create() # record for new solution 

34 new_f = process.f_create() # the objective values 

35 # Obtain the random number generator. 

36 random: Final[Generator] = process.get_random() 

37 

38 # Put function references in variables to save time. 

39 evaluate: Final[Callable] = process.f_evaluate # the objective 

40 op1: Final[Callable] = self.op1.op1 # the unary operator 

41 should_terminate: Final[Callable] = process.should_terminate 

42 domination: Final[Callable] = process.f_dominates 

43 

44 # Start at a random point in the search space and evaluate it. 

45 self.op0.op0(random, best_x) # Create 1 solution randomly and 

46 evaluate(best_x, best_f) # evaluate it. 

47 

48 while not should_terminate(): # Until we need to quit... 

49 op1(random, new_x, best_x) # new_x = neighbor of best_x 

50 evaluate(new_x, new_f) 

51 if domination(new_f, best_f) <= 0: # new is not worse than best? 

52 best_f, new_f = new_f, best_f # swap objective values. 

53 best_x, new_x = new_x, best_x # swap best and new. 

54 

55 process.check_in(best_x, best_f) # check-in final result 

56 

57 def __init__(self, op0: Op0, op1: Op1) -> None: 

58 """ 

59 Create the randomized local search (rls). 

60 

61 :param op0: the nullary search operator 

62 :param op1: the unary search operator 

63 """ 

64 Algorithm1.__init__(self, "morls", op0, op1) 

65 

66 def initialize(self) -> None: 

67 """Initialize the algorithm.""" 

68 Algorithm1.initialize(self)