Skip to content

Commit

Permalink
Resolve merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
ajnebro committed Aug 26, 2020
2 parents b83a594 + c3a7be2 commit 45d87fc
Show file tree
Hide file tree
Showing 22 changed files with 36,145 additions and 25 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: 3 additions & 3 deletions examples/multiobjective/moead/moeaddra_lz09.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
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 = 300000

Expand Down
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
4 changes: 2 additions & 2 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,7 +71,7 @@ def get_name(self) -> str:


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

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
11 changes: 4 additions & 7 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
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
64 changes: 64 additions & 0 deletions jmetal/problem/multiobjective/uf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from math import pi, sin, sqrt

from jmetal.core.problem import FloatProblem
from jmetal.core.solution import FloatSolution

"""
.. module:: UF
:platform: Unix, Windows
:synopsis: Problems of the CEC2009 multi-objective competition
.. moduleauthor:: Antonio J. Nebro <[email protected]>
"""


class UF1(FloatProblem):
""" Problem UF1.
.. note:: Unconstrained problem. The default number of variables is 30.
"""

def __init__(self, number_of_variables: int = 30):
""" :param number_of_variables: number of decision variables of the problem.
"""
super(UF1, self).__init__()
self.number_of_variables = number_of_variables
self.number_of_objectives = 2
self.number_of_constraints = 0

self.obj_directions = [self.MINIMIZE] * self.number_of_objectives
self.obj_labels = ['$ f_{} $'.format(i) for i in range(self.number_of_objectives)]

self.lower_bound = self.number_of_variables * [-1.0]
self.upper_bound = self.number_of_variables * [1.0]
self.lower_bound[0] = 0.0
self.upper_bound[0] = 1.0

def evaluate(self, solution: FloatSolution) -> FloatSolution:
sum1 = 0
sum2 = 0
count1 = 0
count2 = 0

x = solution.variables

for i in range(2, self.number_of_variables):
y = x[i-1] - sin(6.0 * pi * x[0] + i * pi/solution.number_of_variables)
y = y*y

if i % 2 is 0:
sum2 += y
count2 +=1
else:
sum1 +=y
count1 += 1

solution.objectives[0] = x[0] + 2.0 * sum1 /(1.0 * count1)
solution.objectives[1] = 1.0 - sqrt(x[0]) + 2.0 * sum2 / (1.0 * count2)


return solution

def get_name(self):
return 'UF1'

Loading

0 comments on commit 45d87fc

Please sign in to comment.