import numpy as np
import pkgutil
import io
from ddmtolab.Methods.mtop import MTOP
[docs]
class CEC19_MaTMO:
"""
Implementation of CEC19 MaTMO (Many-Task Multi-Objective) benchmark problems P1-P6.
CRITICAL: P1, P4, P6 use DTLZ formulation
P2, P3, P5 use ZDT formulation
Notes
-----
Fixed parameters by benchmark definition:
- D=50 (decision variables)
- M=2 (number of objectives)
- K is configurable (default is 10)
Attributes
----------
data_dir : str
The directory path for problem data files.
"""
problem_information = {
'n_cases': 6,
'n_tasks': 'K',
'n_dims': '50',
'n_objs': '2',
'n_cons': '0',
'type': 'synthetic',
}
def __init__(self):
self.data_dir = 'data_cec19matmo'
def _load_shift_rotation(self, problem_id, task_id):
"""Load shift vector and rotation matrix."""
shift_path = f'{self.data_dir}/SVector/S{problem_id}/S{problem_id}_{task_id}.txt'
rotation_path = f'{self.data_dir}/M/M{problem_id}/M{problem_id}_{task_id}.txt'
shift_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', shift_path)
shift_vector = np.loadtxt(io.BytesIO(shift_bytes))
rotation_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', rotation_path)
rotation_matrix = np.loadtxt(io.BytesIO(rotation_bytes))
return shift_vector, rotation_matrix
def _eval_g_function(self, x, shift_vector, rotation_matrix, g_type, lb, ub):
"""Evaluate g-function with shift, rotation, and boundary handling."""
x = np.atleast_2d(x)
# Apply shift and rotation - MUST use .T
z = (x - shift_vector) @ rotation_matrix.T
# Apply boundary constraints
z = np.clip(z, lb, ub)
if g_type == 'Sphere':
g = np.sum(z ** 2, axis=1)
elif g_type == 'Rosenbrock':
g = np.sum(100 * (z[:, :-1] ** 2 - z[:, 1:]) ** 2 + (1 - z[:, :-1]) ** 2, axis=1)
elif g_type == 'Ackley':
n = z.shape[1]
sum_sq = np.sum(z ** 2, axis=1) / n
sum_cos = np.sum(np.cos(2 * np.pi * z), axis=1) / n
g = -20 * np.exp(-0.2 * np.sqrt(sum_sq)) - np.exp(sum_cos) + 20 + np.e
elif g_type == 'Griewank':
n = z.shape[1]
indices = np.sqrt(np.arange(1, n + 1))
sum_sq = np.sum(z ** 2, axis=1)
prod_cos = np.prod(np.cos(z / indices), axis=1)
g = 1 + sum_sq / 4000 - prod_cos
elif g_type == 'Rastrigin':
n = z.shape[1]
a = 10 * n
g = np.sum(z ** 2 - 10 * np.cos(2 * np.pi * z), axis=1) + a
elif g_type == 'Mean':
n = z.shape[1]
g = 9 * np.sum(np.abs(z), axis=1) / n
else:
raise ValueError(f"Unknown g_type: {g_type}")
return g
def _eval_f1(self, x, f1_type):
"""Evaluate f1 function (for ZDT-type problems)."""
x = np.atleast_2d(x)
if f1_type == 'linear':
# Mean of convergence variables
f1 = np.mean(x, axis=1)
else: # 'nonlinear'
sum_sq = np.sum(x ** 2, axis=1)
r = np.sqrt(sum_sq)
f1 = 1 - np.exp(-4 * r) * (np.sin(5 * np.pi * r) ** 4)
return f1
def _eval_h(self, f1, g, h_type):
"""Evaluate h function for ZDT-type problems."""
if h_type == 'convex':
h = 1 - np.sqrt(f1 / g)
elif h_type == 'concave':
h = 1 - (f1 / g) ** 2
else:
raise ValueError(f"Unknown h_type: {h_type}")
return h
[docs]
def P1(self, K=10) -> MTOP:
"""
P1: Sphere + Circular PF (DTLZ formulation)
- Dimension: 50
- g-function: Sphere
- PF: Circular (DTLZ-style)
"""
problem_id = 1
dim = 50
lb = np.full(dim, -100.0)
ub = np.full(dim, 100.0)
lb[0] = 0.0
ub[0] = 1.0
g_type = 'Sphere'
problem = MTOP()
for task_id in range(1, K + 1):
shift_vector, rotation_matrix = self._load_shift_rotation(problem_id, task_id)
def create_task_func(sv, rm):
def task_func(x):
x = np.atleast_2d(x)
# DTLZ formulation
g = self._eval_g_function(x[:, 1:], sv, rm, g_type, lb[1:], ub[1:])
f1 = (1 + g) * np.cos(x[:, 0] * 0.5 * np.pi)
f2 = (1 + g) * np.sin(x[:, 0] * 0.5 * np.pi)
return np.column_stack([f1, f2])
return task_func
task_func = create_task_func(shift_vector, rotation_matrix)
problem.add_task(task_func, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P2(self, K=10) -> MTOP:
"""
P2: Mean + Concave PF (ZDT formulation)
- Dimension: 50
- g-function: Mean
- PF: Concave
"""
problem_id = 2
dim = 50
lb = np.full(dim, -100.0)
ub = np.full(dim, 100.0)
lb[0] = 0.0
ub[0] = 1.0
g_type = 'Mean'
f1_type = 'linear'
h_type = 'concave'
problem = MTOP()
for task_id in range(1, K + 1):
shift_vector, rotation_matrix = self._load_shift_rotation(problem_id, task_id)
def create_task_func(sv, rm):
def task_func(x):
x = np.atleast_2d(x)
# ZDT formulation
f1 = self._eval_f1(x[:, 0:1], f1_type)
g = self._eval_g_function(x[:, 1:], sv, rm, g_type, lb[1:], ub[1:])
g = g + 1
f2 = g * self._eval_h(f1, g, h_type)
return np.column_stack([f1, f2])
return task_func
task_func = create_task_func(shift_vector, rotation_matrix)
problem.add_task(task_func, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P3(self, K=10) -> MTOP:
"""
P3: Rosenbrock + Concave PF (ZDT formulation)
- Dimension: 10
- g-function: Rosenbrock
- PF: Concave
"""
problem_id = 3
dim = 10
lb = np.full(dim, -5.0)
ub = np.full(dim, 5.0)
lb[0] = 0.0
ub[0] = 1.0
g_type = 'Rosenbrock'
f1_type = 'linear'
h_type = 'concave'
problem = MTOP()
for task_id in range(1, K + 1):
shift_vector, rotation_matrix = self._load_shift_rotation(problem_id, task_id)
def create_task_func(sv, rm):
def task_func(x):
x = np.atleast_2d(x)
# ZDT formulation
f1 = self._eval_f1(x[:, 0:1], f1_type)
g = self._eval_g_function(x[:, 1:], sv, rm, g_type, lb[1:], ub[1:])
g = g + 1
f2 = g * self._eval_h(f1, g, h_type)
return np.column_stack([f1, f2])
return task_func
task_func = create_task_func(shift_vector, rotation_matrix)
problem.add_task(task_func, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P4(self, K=10) -> MTOP:
"""
P4: Rastrigin + Circular PF (DTLZ formulation)
- Dimension: 50
- g-function: Rastrigin
- PF: Circular (DTLZ-style)
"""
problem_id = 4
dim = 50
lb = np.full(dim, -2.0)
ub = np.full(dim, 2.0)
lb[0] = 0.0
ub[0] = 1.0
g_type = 'Rastrigin'
problem = MTOP()
for task_id in range(1, K + 1):
shift_vector, rotation_matrix = self._load_shift_rotation(problem_id, task_id)
def create_task_func(sv, rm):
def task_func(x):
x = np.atleast_2d(x)
# DTLZ formulation
g = self._eval_g_function(x[:, 1:], sv, rm, g_type, lb[1:], ub[1:])
f1 = (1 + g) * np.cos(x[:, 0] * 0.5 * np.pi)
f2 = (1 + g) * np.sin(x[:, 0] * 0.5 * np.pi)
return np.column_stack([f1, f2])
return task_func
task_func = create_task_func(shift_vector, rotation_matrix)
problem.add_task(task_func, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P5(self, K=10) -> MTOP:
"""
P5: Ackley + Convex PF (ZDT formulation)
- Dimension: 50
- g-function: Ackley
- PF: Convex
"""
problem_id = 5
dim = 50
lb = np.full(dim, -1.0)
ub = np.full(dim, 1.0)
lb[0] = 0.0
ub[0] = 1.0
g_type = 'Ackley'
f1_type = 'linear'
h_type = 'convex'
problem = MTOP()
for task_id in range(1, K + 1):
shift_vector, rotation_matrix = self._load_shift_rotation(problem_id, task_id)
def create_task_func(sv, rm):
def task_func(x):
x = np.atleast_2d(x)
# ZDT formulation
f1 = self._eval_f1(x[:, 0:1], f1_type)
g = self._eval_g_function(x[:, 1:], sv, rm, g_type, lb[1:], ub[1:])
g = g + 1
f2 = g * self._eval_h(f1, g, h_type)
return np.column_stack([f1, f2])
return task_func
task_func = create_task_func(shift_vector, rotation_matrix)
problem.add_task(task_func, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P6(self, K=10) -> MTOP:
"""
P6: Griewank + Circular PF (DTLZ formulation)
- Dimension: 50
- g-function: Griewank
- PF: Circular (DTLZ-style)
"""
problem_id = 6
dim = 50
lb = np.full(dim, -50.0)
ub = np.full(dim, 50.0)
lb[0] = 0.0
ub[0] = 1.0
g_type = 'Griewank'
problem = MTOP()
for task_id in range(1, K + 1):
shift_vector, rotation_matrix = self._load_shift_rotation(problem_id, task_id)
def create_task_func(sv, rm):
def task_func(x):
x = np.atleast_2d(x)
# DTLZ formulation
g = self._eval_g_function(x[:, 1:], sv, rm, g_type, lb[1:], ub[1:])
f1 = (1 + g) * np.cos(x[:, 0] * 0.5 * np.pi)
f2 = (1 + g) * np.sin(x[:, 0] * 0.5 * np.pi)
return np.column_stack([f1, f2])
return task_func
task_func = create_task_func(shift_vector, rotation_matrix)
problem.add_task(task_func, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
# Pareto Front functions
def P1_PF(N, M=2):
"""Circular PF for P1"""
f1 = np.linspace(0, 1, N)
f2 = np.sqrt(1 - f1 ** 2)
return np.column_stack([f1, f2])
def P2_PF(N, M=2):
"""Concave PF for P2"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
def P3_PF(N, M=2):
"""Concave PF for P3"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
def P4_PF(N, M=2):
"""Circular PF for P4"""
f1 = np.linspace(0, 1, N)
f2 = np.sqrt(1 - f1 ** 2)
return np.column_stack([f1, f2])
def P5_PF(N, M=2):
"""Convex PF for P5"""
f1 = np.linspace(0, 1, N)
f2 = 1 - np.sqrt(f1)
return np.column_stack([f1, f2])
def P6_PF(N, M=2):
"""Circular PF for P6"""
f1 = np.linspace(0, 1, N)
f2 = np.sqrt(1 - f1 ** 2)
return np.column_stack([f1, f2])
SETTINGS = {
'metric': 'IGD',
'n_pf': 10000,
'pf_path': './MOReference',
'P1': {'all_tasks': P1_PF},
'P2': {'all_tasks': P2_PF},
'P3': {'all_tasks': P3_PF},
'P4': {'all_tasks': P4_PF},
'P5': {'all_tasks': P5_PF},
'P6': {'all_tasks': P6_PF},
}