Skip to content

Commit

Permalink
jMetalPy v1.5.5 (#79)
Browse files Browse the repository at this point in the history
* Add solution generator and evaluator for SA (#67)

* Add warm startup for SA using population_generator.

* Revert evaluator parametrization in SA.

* Working on implementing a IntegerFloatSolution class

* Update nsgaiii.py (#70)

removed obsolete import

* Fix conflict in file smpsorp_zdt4.py

* Add new implementation of quality indicators. All of them receive a numpy array as a parameter instead of a list of solutions.

* Refactor quality indicators. All of them receive as a parameter a numpy array instead of a list of solutions

* Add file ZDT1.pf in the test folder (to be use to test quality indicators)

* Feature/mixed solution (#73)

* Working on implementing a IntegerFloatSolution class

* Add unit test cases for class IntegerFloatProblem

* Add class NMMin

* Add class IntegerFloatSBXCrossover

* Add test cases for SBXCrossover

* Still working on implementing an approach for the IntegerFloatSolution class

* Add user defined exceptiones in file checking.py

* Working on the implementation of class CompositeSolution

* Workon on class CompositeSolution

* Class CompositeMutation implemented and tested

* Fix a bug in class Neighborhood

* Class CompositeCrossover implemented and tested

* Add class

* Add class

* Rename file

* Add problem ZDT1Modified

* Add examples with NSGA-II

* Add NSGA-II examples

* Optimize imports

* Minor changes

* Changes on attribute name

Co-authored-by: Yebisu <ajnebro@outlook.com>

* Minor changes

* Release v1.5.4

* Updating the MOEAD and variants examples

* Minor changes

* Add problem UF1

* Fix bug in ibea.

* Refactor crossover and mutation operators

* Refactor crossover and mutation operators

* New minor version 1.5.5

Co-authored-by: Yevhenii Semendiak <32543098+YevheniiSemendiak@users.noreply.github.com>
Co-authored-by: ajnebro <ajnebro@outlook.com>
Co-authored-by: Marvin Steijaert <msteijaert@gmail.com>
Co-authored-by: benhid <atbnhd@gmail.com>
Co-authored-by: Sizhe Yuen <sizhe1007@gmail.com>
Co-authored-by: Antonio J. Nebro <ajnebro@users.noreply.github.com>
  • Loading branch information
7 people authored Apr 9, 2020
1 parent 6f54940 commit c3a7be2
Show file tree
Hide file tree
Showing 25 changed files with 36,163 additions and 32 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ plot_front.plot(front, label='NSGAII-ZDT1', filename='NSGAII-ZDT1', format='png'
<img src=docs/source/_static/NSGAII-ZDT1.png width=450 alt="Pareto front approximation">

## Features
The current release of jMetalPy (v1.5.4) contains the following components:
The current release of jMetalPy (v1.5.5) contains the following components:

* Algorithms: local search, genetic algorithm, evolution strategy, simulated annealing, random search, NSGA-II, NSGA-III, SMPSO, OMOPSO, MOEA/D, MOEA/D-DRA, MOEA/D-IEpsilon, GDE3, SPEA2, HYPE, IBEA. Preference articulation-based algorithms (G-NSGA-II, G-GDE3, G-SPEA2, SMPSO/RP); Dynamic versions of NSGA-II, SMPSO, and GDE3.
* Parallel computing based on Apache Spark and Dask.
Expand All @@ -118,6 +118,7 @@ The current release of jMetalPy (v1.5.4) contains the following components:

## Changelog

* [v1.5.5] Minor bug fixes.
* [v1.5.4] Refactored quality indicators to accept numpy array as input parameter.
* [v1.5.4] Added [CompositeSolution](https://github.com/jMetal/jMetalPy/blob/master/jmetal/core/solution.py#L111) class to support mixed combinatorial problems. [#69](https://github.com/jMetal/jMetalPy/issues/69)

Expand Down
6 changes: 5 additions & 1 deletion examples/multiobjective/moead/moead_dtlz2.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from jmetal.algorithm.multiobjective.moead import MOEAD
from jmetal.core.quality_indicator import HyperVolume, InvertedGenerationalDistance
from jmetal.operator import PolynomialMutation, DifferentialEvolutionCrossover
from jmetal.problem import DTLZ2
from jmetal.util.aggregative_function import Tschebycheff
Expand All @@ -14,7 +15,7 @@
algorithm = MOEAD(
problem=problem,
population_size=300,
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5, K=0.5),
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5),
mutation=PolynomialMutation(probability=1.0 / problem.number_of_variables, distribution_index=20),
aggregative_function=Tschebycheff(dimension=problem.number_of_objectives),
neighbor_size=20,
Expand All @@ -27,6 +28,9 @@
algorithm.run()
front = algorithm.get_result()

hypervolume = HyperVolume([1.0, 1.0, 1.0])
print("Hypervolume: " + str(hypervolume.compute([front[i].objectives for i in range(len(front))])))

# Save results to file
print_function_values_to_file(front, 'FUN.' + algorithm.label)
print_variables_to_file(front, 'VAR.'+ algorithm.label)
Expand Down
2 changes: 1 addition & 1 deletion examples/multiobjective/moead/moead_iepsilon_lircmop1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
algorithm = MOEADIEpsilon(
problem=problem,
population_size=300,
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5, K=0.5),
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5),
mutation=PolynomialMutation(probability=1.0 / problem.number_of_variables, distribution_index=20),
aggregative_function=Tschebycheff(dimension=problem.number_of_objectives),
neighbor_size=20,
Expand Down
2 changes: 1 addition & 1 deletion examples/multiobjective/moead/moead_lz09.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
algorithm = MOEAD(
problem=problem,
population_size=300,
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5, K=0.5),
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5),
mutation=PolynomialMutation(probability=1.0 / problem.number_of_variables, distribution_index=20),
aggregative_function=Tschebycheff(dimension=problem.number_of_objectives),
neighbor_size=20,
Expand Down
17 changes: 12 additions & 5 deletions examples/multiobjective/moead/moeaddra_lz09.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
from jmetal.algorithm.multiobjective.moead import MOEAD_DRA
from jmetal.operator import PolynomialMutation, DifferentialEvolutionCrossover
from jmetal.problem import LZ09_F2
from jmetal.problem.multiobjective.uf import UF1
from jmetal.util.aggregative_function import Tschebycheff
from jmetal.util.solution import read_solutions, print_function_values_to_file, print_variables_to_file
from jmetal.util.termination_criterion import StoppingByEvaluations
from jmetal.core.quality_indicator import HyperVolume, InvertedGenerationalDistance

if __name__ == '__main__':
problem = LZ09_F2()
problem.reference_front = read_solutions(filename='resources/reference_front/LZ09_F2.pf')
problem = UF1()
problem.reference_front = read_solutions(filename='resources/reference_front/UF1.pf')

max_evaluations = 150000
max_evaluations = 300000

algorithm = MOEAD_DRA(
problem=problem,
population_size=300,
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5, K=0.5),
population_size=600,
crossover=DifferentialEvolutionCrossover(CR=1.0, F=0.5),
mutation=PolynomialMutation(probability=1.0 / problem.number_of_variables, distribution_index=20),
aggregative_function=Tschebycheff(dimension=problem.number_of_objectives),
neighbor_size=20,
Expand All @@ -27,10 +29,15 @@
algorithm.run()
front = algorithm.get_result()

hypervolume = HyperVolume([2.0, 2.0])
print("Hypervolume: " + str(hypervolume.compute([front[i].objectives for i in range(len(front))])))

# Save results to file
print_function_values_to_file(front, 'FUN.' + algorithm.label)
print_variables_to_file(front, 'VAR.'+ algorithm.label)

print(f'Algorithm: ${algorithm.get_name()}')
print(f'Problem: ${problem.get_name()}')
print(f'Computing time: ${algorithm.total_computing_time}')


4 changes: 2 additions & 2 deletions jmetal/algorithm/multiobjective/ibea.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def compute_fitness_values(self, population: List[S], kappa: float) -> List[S]:
for j in range(len(population)):
if j != i:
population[i].attributes['fitness'] += -np.exp(
-EpsilonIndicator([population[i]]).compute([population[j]]) / self.kappa)
-EpsilonIndicator([population[i].objectives]).compute([population[j].objectives]) / self.kappa)
return population

def create_initial_solutions(self) -> List[S]:
Expand All @@ -92,7 +92,7 @@ def replacement(self, population: List[S], offspring_population: List[S]) -> Lis

for i in range(join_population_size):
join_population[i].attributes['fitness'] += np.exp(
- EpsilonIndicator([join_population[i]]).compute([join_population[index_worst]]) / self.kappa)
- EpsilonIndicator([join_population[i].objectives]).compute([join_population[index_worst].objectives]) / self.kappa)

join_population.pop(index_worst)
join_population_size = join_population_size - 1
Expand Down
3 changes: 2 additions & 1 deletion jmetal/algorithm/singleobjective/evolution_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from jmetal.util.evaluator import Evaluator, SequentialEvaluator
from jmetal.util.generator import Generator, RandomGenerator
from jmetal.util.termination_criterion import TerminationCriterion
from jmetal.util.constraint_handling import overall_constraint_violation_degree

S = TypeVar('S')
R = TypeVar('R')
Expand Down Expand Up @@ -78,7 +79,7 @@ def replacement(self, population: List[S], offspring_population: List[S]) -> Lis
else:
population_pool.extend(offspring_population)

population_pool.sort(key=lambda s: s.objectives[0])
population_pool.sort(key=lambda s: (overall_constraint_violation_degree(s), s.objectives[0]))

new_population = []
for i in range(self.mu):
Expand Down
6 changes: 3 additions & 3 deletions jmetal/core/quality_indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_short_name(self) -> str:


class GenerationalDistance(QualityIndicator):
def __init__(self, reference_front: np.array=None):
def __init__(self, reference_front: np.array = None):
"""
* Van Veldhuizen, D.A., Lamont, G.B.: Multiobjective Evolutionary Algorithm Research: A History and Analysis.
Technical Report TR-98-03, Dept. Elec. Comput. Eng., Air Force. Inst. Technol. (1998)
Expand All @@ -71,11 +71,11 @@ def get_name(self) -> str:


class InvertedGenerationalDistance(QualityIndicator):
def __init__(self, reference_front: np.array):
def __init__(self, reference_front: np.array = None):
super(InvertedGenerationalDistance, self).__init__(is_minimization=True)
self.reference_front = reference_front

def compute(self, solutions: np.array = None):
def compute(self, solutions: np.array):
if self.reference_front is None:
raise Exception('Reference front is none')

Expand Down
6 changes: 3 additions & 3 deletions jmetal/core/test/test_solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ def test_should_copy_work_properly(self) -> None:
self.assertEqual(solution.attributes, new_solution.attributes)



class IntegerSolutionTestCase(unittest.TestCase):

def test_should_constructor_create_a_non_null_object(self) -> None:
Expand Down Expand Up @@ -119,6 +118,7 @@ def test_should_copy_work_properly(self) -> None:
self.assertIs(solution.upper_bound, solution.upper_bound)
self.assertEqual(solution.attributes, new_solution.attributes)


class CompositeSolutionTestCase(unittest.TestCase):
def test_should_constructor_create_a_valid_not_none_composite_solution_composed_of_a_double_solution(self):
composite_solution = CompositeSolution([FloatSolution([1.0], [2.0], 2)])
Expand All @@ -134,7 +134,7 @@ def test_should_constructor_raise_an_exception_if_the_number_of_objectives_is_no
def test_should_constructor_create_a_valid_soltion_composed_of_a_float_and_an_integer_solutions(self):
number_of_objectives = 3
number_of_constraints = 1
float_solution: FloatSolution = FloatSolution([1.0], [3.0], number_of_objectives, number_of_constraints)
float_solution: FloatSolution = FloatSolution([1.0], [3.0], number_of_objectives, number_of_constraints)
integer_solution: IntegerSolution = IntegerSolution([2], [4], number_of_objectives, number_of_constraints)

solution: CompositeSolution = CompositeSolution([float_solution, integer_solution])
Expand All @@ -153,7 +153,7 @@ def test_should_constructor_create_a_valid_soltion_composed_of_a_float_and_an_in
def test_should_copy_work_properly(self):
number_of_objectives = 3
number_of_constraints = 1
float_solution: FloatSolution = FloatSolution([1.0], [3.0], number_of_objectives, number_of_constraints)
float_solution: FloatSolution = FloatSolution([1.0], [3.0], number_of_objectives, number_of_constraints)
integer_solution: IntegerSolution = IntegerSolution([2], [4], number_of_objectives, number_of_constraints)

solution: CompositeSolution = CompositeSolution([float_solution, integer_solution])
Expand Down
13 changes: 5 additions & 8 deletions jmetal/operator/crossover.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@


class NullCrossover(Crossover[Solution, Solution]):

def __init__(self):
super(NullCrossover, self).__init__(probability=0.0)

Expand All @@ -38,7 +37,6 @@ def get_name(self):


class PMXCrossover(Crossover[PermutationSolution, PermutationSolution]):

def __init__(self, probability: float):
super(PMXCrossover, self).__init__(probability=probability)

Expand Down Expand Up @@ -97,7 +95,6 @@ def get_name(self):


class CXCrossover(Crossover[PermutationSolution, PermutationSolution]):

def __init__(self, probability: float):
super(CXCrossover, self).__init__(probability=probability)

Expand Down Expand Up @@ -148,8 +145,8 @@ def __init__(self, probability: float, distribution_index: float = 20.0):
raise Exception("The distribution index is negative: " + str(distribution_index))

def execute(self, parents: List[FloatSolution]) -> List[FloatSolution]:
Check.that(type(parents[0]) is FloatSolution, "Solution type invalid: " + str(type(parents[0])))
Check.that(type(parents[1]) is FloatSolution, "Solution type invalid")
Check.that(issubclass(type(parents[0]), FloatSolution), "Solution type invalid: " + str(type(parents[0])))
Check.that(issubclass(type(parents[1]), FloatSolution), "Solution type invalid")
Check.that(len(parents) == 2, 'The number of parents is not two: {}'.format(len(parents)))

offspring = [copy.deepcopy(parents[0]), copy.deepcopy(parents[1])]
Expand Down Expand Up @@ -229,8 +226,8 @@ def __init__(self, probability: float, distribution_index: float = 20.0):
self.distribution_index = distribution_index

def execute(self, parents: List[IntegerSolution]) -> List[IntegerSolution]:
Check.that(type(parents[0]) is IntegerSolution, "Solution type invalid")
Check.that(type(parents[1]) is IntegerSolution, "Solution type invalid")
Check.that(issubclass(type(parents[0]), IntegerSolution), "Solution type invalid")
Check.that(issubclass(type(parents[1]), IntegerSolution), "Solution type invalid")
Check.that(len(parents) == 2, 'The number of parents is not two: {}'.format(len(parents)))

offspring = [copy.deepcopy(parents[0]), copy.deepcopy(parents[1])]
Expand Down Expand Up @@ -369,7 +366,7 @@ class DifferentialEvolutionCrossover(Crossover[FloatSolution, FloatSolution]):
external to the crossover operator.
"""

def __init__(self, CR: float, F: float, K: float):
def __init__(self, CR: float, F: float, K: float = 0.5):
super(DifferentialEvolutionCrossover, self).__init__(probability=1.0)
self.CR = CR
self.F = F
Expand Down
4 changes: 2 additions & 2 deletions jmetal/operator/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(self, probability: float, distribution_index: float = 0.20):
self.distribution_index = distribution_index

def execute(self, solution: FloatSolution) -> FloatSolution:
Check.that(type(solution) is FloatSolution, "Solution type invalid")
Check.that(issubclass(type(solution), FloatSolution), "Solution type invalid")
for i in range(solution.number_of_variables):
rand = random.random()

Expand Down Expand Up @@ -98,7 +98,7 @@ def __init__(self, probability: float, distribution_index: float = 0.20):
self.distribution_index = distribution_index

def execute(self, solution: IntegerSolution) -> IntegerSolution:
Check.that(type(solution) is IntegerSolution, "Solution type invalid")
Check.that(issubclass(type(solution), IntegerSolution), "Solution type invalid")

for i in range(solution.number_of_variables):
if random.random() <= self.probability:
Expand Down
24 changes: 23 additions & 1 deletion jmetal/operator/test/test_crossover.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from typing import List
from unittest import mock

from jmetal.core.operator import Crossover
Expand Down Expand Up @@ -294,6 +295,27 @@ def test_should_execute_return_the_parents_if_the_crossover_probability_is_zero(
self.assertEqual(solution1.variables, offspring[0].variables)
self.assertEqual(solution2.variables, offspring[1].variables)

def test_should_execute_work_with_a_solution_subclass_of_float_solution(self):
class NewFloatSolution(FloatSolution):
def __init__(self, lower_bound: List[float], upper_bound: List[float], number_of_objectives: int,
number_of_constraints: int = 0):
super(NewFloatSolution, self).__init__(lower_bound, upper_bound, number_of_objectives,
number_of_constraints)

solution1 = NewFloatSolution([1, 2], [2, 4], 2, 2)
solution2 = NewFloatSolution([1, 2], [2, 4], 2, 2)

solution1.variables = [1.5, 2.7]
solution2.variables = [1.7, 3.6]

crossover: SBXCrossover = SBXCrossover(0.0, 20.0)
offspring = crossover.execute([solution1, solution2])

self.assertEqual(2, len(offspring))
self.assertEqual(solution1.variables, offspring[0].variables)
self.assertEqual(solution2.variables, offspring[1].variables)


def test_should_execute_produce_valid_solutions_when_crossing_two_single_variable_solutions(self):
pass

Expand All @@ -308,7 +330,7 @@ def test_should_constructor_raise_an_exception_if_the_parameter_list_is_Empty(se
CompositeCrossover([])

def test_should_constructor_create_a_valid_operator_when_adding_a_single_crossover_operator(self):
crossover: Crossover = SBXCrossover(0.9, 20.0)
crossover: Crossover = SBXCrossover(0.9, 20.0)

operator = CompositeCrossover([crossover])
self.assertIsNotNone(operator)
Expand Down
18 changes: 16 additions & 2 deletions jmetal/operator/test/test_mutation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from typing import List

from jmetal.core.operator import Mutation
from jmetal.core.solution import BinarySolution, FloatSolution, IntegerSolution, CompositeSolution
Expand Down Expand Up @@ -46,8 +47,21 @@ def test_should_the_solution_change__if_the_probability_is_one(self):
operator = PolynomialMutation(1.0)
solution = FloatSolution([-5, -5, -5], [5, 5, 5], 2)
solution.variables = [1.0, 2.0, 3.0]
FloatSolution.lower_bound = [-5, -5, -5]
FloatSolution.upper_bound = [5, 5, 5]

mutated_solution = operator.execute(solution)

self.assertNotEqual([1.0, 2.0, 3.0], mutated_solution.variables)

def test_should_execute_work_with_a_solution_subclass_of_float_solution(self):
class NewFloatSolution(FloatSolution):
def __init__(self, lower_bound: List[float], upper_bound: List[float], number_of_objectives: int,
number_of_constraints: int = 0):
super(NewFloatSolution, self).__init__(lower_bound, upper_bound, number_of_objectives,
number_of_constraints)

operator = PolynomialMutation(1.0)
solution = NewFloatSolution([-5, -5, -5], [5, 5, 5], 2)
solution.variables = [1.0, 2.0, 3.0]

mutated_solution = operator.execute(solution)

Expand Down
Loading

0 comments on commit c3a7be2

Please sign in to comment.