import scipy.io
import pkgutil
import io
import numpy as np
from ddmtolab.Methods.mtop import MTOP
[docs]
class CEC17MTMO:
"""
Implementation of the CEC 2017 Competition on Evolutionary Multi-Task Multi-Objective
Optimization (MTMO) benchmark problems P1 to P9.
These problems consist of two multi-objective optimization tasks (MO-tasks)
with shared variables, designed to test knowledge transfer in the presence of
multiple conflicting objectives. All tasks are minimization problems.
Notes
-----
Fixed parameters by benchmark definition:
- K=2 (number of tasks)
- D and M vary by problem (see individual method docstrings)
Attributes
----------
data_dir : str
The directory path for problem data files.
"""
problem_information = {
'n_cases': 9,
'n_tasks': '2',
'n_dims': '[10, 50]',
'n_objs': '[2, 3]',
'n_cons': '0',
'type': 'synthetic',
}
def __init__(self):
self.data_dir = 'data_cec17mtmo'
[docs]
def P1(self) -> MTOP:
"""
Generates Problem 1: **T1 (ZDT3-like) vs T2 (ZDT2-like)**.
Both tasks are 2-objective, 50-dimensional.
- T1: Modified ZDT3-like, PF is discontinuous, non-convex (Curved, Piecewise).
- T2: Modified ZDT2-like, PF is continuous, non-convex.
- Relationship: Decision space overlap exists only in :math:`x_1` dimension (:math:`[0, 1]`).
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 50
def T1(x):
x = np.atleast_2d(x)
q = 1.0 + np.sum(x[:, 1:] ** 2, axis=1)
x1 = x[:, 0]
f1 = q * np.cos(np.pi * x1 / 2)
f2 = q * np.sin(np.pi * x1 / 2)
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
q = 1.0 + 9.0 / (dim - 1) * np.sum(np.abs(x[:, 1:]), axis=1)
x1 = x[:, 0]
f1 = x1
f2 = q * (1.0 - (x1 / q) ** 2)
return np.vstack([f1, f2]).T
lb = np.array([0.0] + [-100.0] * (dim - 1))
ub = np.array([1.0] + [100.0] * (dim - 1))
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb, upper_bound=ub)
problem.add_task(T2, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P2(self) -> MTOP:
"""
Generates Problem 2: **T1 (ZDT2-like, Rosenbrock) vs T2 (ZDT3-like, Rotated)**.
Both tasks are 2-objective, 10-dimensional.
- T1: Modified ZDT2-like with Rosenbrock-like component.
- T2: Modified ZDT3-like with rotated non-linear component (Mcm2).
- Relationship: Decision variables are coupled and search spaces are different.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 10
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Mcm2.mat')
mat_file = io.BytesIO(data_bytes)
Mcm2 = scipy.io.loadmat(mat_file)['Mcm2']
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Scm2.mat')
mat_file = io.BytesIO(data_bytes)
Scm2 = scipy.io.loadmat(mat_file)['Scm2'].flatten()
def T1(x):
x = np.atleast_2d(x)
# Rosenbrock-like component in g-function
q = 1 + np.sum(100 * ((x[:, 1:-1] ** 2 - x[:, 2:]) ** 2 + (1 - x[:, 1:-1]) ** 2), axis=1)
f1 = x[:, 0]
f2 = q * (1 - (x[:, 0] / q) ** 2)
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
# Shifted and rotated component
z = (x[:, 1:] - Scm2) @ Mcm2.T
q = 1 + 9 / (dim - 1) * np.sum(np.abs(z), axis=1)
f1 = q * np.cos(np.pi * x[:, 0] / 2)
f2 = q * np.sin(np.pi * x[:, 0] / 2)
return np.vstack([f1, f2]).T
lb = np.array([0.0] + [-5.0] * (dim - 1))
ub = np.array([1.0] + [5.0] * (dim - 1))
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb, upper_bound=ub)
problem.add_task(T2, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P3(self) -> MTOP:
"""
Generates Problem 3: **T1 (ZDT3-like, Rastrigin) vs T2 (ZDT1-like, Ackley)**.
Both tasks are 2-objective, 50-dimensional.
- T1: Modified ZDT3-like with Rastrigin-like component in :math:`g`. PF is discontinuous, non-convex.
- T2: Modified ZDT1-like with Ackley-like component in :math:`g`. PF is continuous, convex.
- Relationship: Tasks have different search spaces and different :math:`g`-functions.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 50
def T1(x):
x = np.atleast_2d(x)
part = x[:, 1:]
# Rastrigin-like component in g
q = 1.0 + np.sum(part ** 2 - 10.0 * np.cos(2.0 * np.pi * part) + 10.0, axis=1)
f1 = q * np.cos(np.pi * x[:, 0] / 2.0)
f2 = q * np.sin(np.pi * x[:, 0] / 2.0)
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
r = x.shape[1]
part = x[:, 1:]
# Ackley-like component in g
term1 = 21.0 + np.e
sum_sq = np.sum(part ** 2, axis=1)
sum_cos = np.sum(np.cos(2.0 * np.pi * part), axis=1)
q = term1 - 20.0 * np.exp(-0.2 * np.sqrt((1.0 / (r - 1)) * sum_sq)) - np.exp((1.0 / (r - 1)) * sum_cos)
f1 = x[:, 0]
f2 = q * (1.0 - np.sqrt(x[:, 0] / q))
return np.vstack([f1, f2]).T
lb1 = np.concatenate(([0.0], -2.0 * np.ones(dim - 1)))
ub1 = np.concatenate(([1.0], 2.0 * np.ones(dim - 1)))
lb2 = np.concatenate(([0.0], -1.0 * np.ones(dim - 1)))
ub2 = np.concatenate(([1.0], 1.0 * np.ones(dim - 1)))
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb1, upper_bound=ub1)
problem.add_task(T2, dim=dim, lower_bound=lb2, upper_bound=ub2)
return problem
[docs]
def P4(self) -> MTOP:
"""
Generates Problem 4: **T1 (ZDT1-like, Sphere) vs T2 (ZDT1-like, Rastrigin)**.
Both tasks are 2-objective, 50-dimensional.
- T1: Modified ZDT1-like with Sphere component in :math:`g`. PF is continuous, convex.
- T2: Modified ZDT1-like with shifted Rastrigin component (Sph2) in :math:`g`. PF is continuous, convex.
- Relationship: The :math:`g`-functions and search spaces are different.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 50
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Sph2.mat')
mat_file = io.BytesIO(data_bytes)
Sph2 = scipy.io.loadmat(mat_file)['Sph2'].flatten()
def T1(x):
x = np.atleast_2d(x)
part = x[:, 1:]
# Sphere component in g
q = 1.0 + np.sum(part ** 2, axis=1)
f1 = x[:, 0]
f2 = q * (1.0 - np.sqrt(x[:, 0] / q))
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
z = x[:, 1:] - Sph2
# Shifted Rastrigin component in g
q = 1.0 + np.sum(z ** 2 - 10.0 * np.cos(2.0 * np.pi * z) + 10.0, axis=1)
f1 = x[:, 0]
f2 = q * (1.0 - np.sqrt(x[:, 0] / q))
return np.vstack([f1, f2]).T
lb = np.array([0.0] + [-100.0] * (dim - 1))
ub = np.array([1.0] + [100.0] * (dim - 1))
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb, upper_bound=ub)
problem.add_task(T2, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P5(self) -> MTOP:
"""
Generates Problem 5: **T1 (ZDT3-like, Rotated Sphere) vs T2 (ZDT2-like, Rotated Rastrigin)**.
Both tasks are 2-objective, 50-dimensional.
- T1: Modified ZDT3-like with rotated and shifted Sphere component in :math:`g` (Mpm1, Spm1). PF is discontinuous, non-convex.
- T2: Modified ZDT2-like with rotated Rastrigin component in :math:`g` (Mpm2). PF is continuous, non-convex.
- Relationship: Different problem landscapes and global optimum locations in the search space.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 50
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Mpm1.mat')
mat_file = io.BytesIO(data_bytes)
Mpm1 = scipy.io.loadmat(mat_file)['Mpm1']
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Spm1.mat')
mat_file = io.BytesIO(data_bytes)
Spm1 = scipy.io.loadmat(mat_file)['Spm1'].flatten()
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Mpm2.mat')
mat_file = io.BytesIO(data_bytes)
Mpm2 = scipy.io.loadmat(mat_file)['Mpm2']
def T1(x):
x = np.atleast_2d(x)
# Rotated and shifted Sphere in g
z = (x[:, 1:] - Spm1) @ Mpm1.T
q = 1.0 + np.sum(z ** 2, axis=1)
a = np.cos(np.pi * x[:, 0] / 2.0)
b = np.sin(np.pi * x[:, 0] / 2.0)
f1 = q * a
f2 = q * b
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
# Rotated Rastrigin in g
z = x[:, 1:] @ Mpm2.T
term = z ** 2 - 10.0 * np.cos(2.0 * np.pi * z) + 10.0
q = 1.0 + np.sum(term, axis=1)
f1 = x[:, 0]
f2 = q * (1.0 - (x[:, 0] / q) ** 2)
return np.vstack([f1, f2]).T
lb = np.zeros(dim)
ub = np.ones(dim)
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb, upper_bound=ub)
problem.add_task(T2, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P6(self) -> MTOP:
"""
Generates Problem 6: **T1 (ZDT3-like, Griewank) vs T2 (ZDT3-like, Ackley)**.
Both tasks are 2-objective, 50-dimensional.
- T1: Modified ZDT3-like with Griewank component in :math:`g`. PF is discontinuous, non-convex.
- T2: Modified ZDT3-like with shifted Ackley component (Spl2) in :math:`g`. PF is discontinuous, non-convex.
- Relationship: PF shapes are similar, but :math:`g`-functions and search spaces are different.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 50
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Spl2.mat')
mat_file = io.BytesIO(data_bytes)
Spl2 = scipy.io.loadmat(mat_file)['Spl2'].flatten()
def T1(x):
x = np.atleast_2d(x)
r = x.shape[1]
# Griewank component in g
sum_sq = np.sum(x[:, 1:] ** 2, axis=1)
denom = np.sqrt(np.arange(1, r))
cos_terms = np.cos(x[:, 1:] / denom)
prod_cos = np.prod(cos_terms, axis=1)
q = 2.0 + (1.0 / 4000.0) * sum_sq - prod_cos
f1 = q * np.cos(np.pi * x[:, 0] / 2.0)
f2 = q * np.sin(np.pi * x[:, 0] / 2.0)
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
r = x.shape[1]
z = x[:, 1:] - Spl2
# Shifted Ackley component in g
sum_sq = np.sum(z ** 2, axis=1)
sum_cos = np.sum(np.cos(2.0 * np.pi * z), axis=1)
q = 21.0 + np.e - 20.0 * np.exp(-0.2 * np.sqrt((1.0 / (r - 1)) * sum_sq)) - np.exp(
(1.0 / (r - 1)) * sum_cos)
f1 = q * np.cos(np.pi * x[:, 0] / 2.0)
f2 = q * np.sin(np.pi * x[:, 0] / 2.0)
return np.vstack([f1, f2]).T
lb1 = np.concatenate(([0.0], -50.0 * np.ones(dim - 1)))
ub1 = np.concatenate(([1.0], 50.0 * np.ones(dim - 1)))
lb2 = np.concatenate(([0.0], -100.0 * np.ones(dim - 1)))
ub2 = np.concatenate(([1.0], 100.0 * np.ones(dim - 1)))
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb1, upper_bound=ub1)
problem.add_task(T2, dim=dim, lower_bound=lb2, upper_bound=ub2)
return problem
[docs]
def P7(self) -> MTOP:
"""
Generates Problem 7: **T1 (ZDT3-like, Rosenbrock) vs T2 (ZDT1-like, Sphere)**.
Both tasks are 2-objective, 50-dimensional.
- T1: Modified ZDT3-like with Rosenbrock component in :math:`g`. PF is discontinuous, non-convex.
- T2: Modified ZDT1-like with Sphere component in :math:`g`. PF is continuous, convex.
- Relationship: Highly multi-modal :math:`g`-function in T1 and different PF shapes.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 50
def T1(x):
x = np.atleast_2d(x)
part = x[:, 1:-1]
next_part = x[:, 2:]
# Rosenbrock component in g
q = 1.0 + np.sum(100.0 * (part ** 2 - next_part) ** 2 + (1.0 - part) ** 2, axis=1)
f1 = q * np.cos(np.pi * x[:, 0] / 2.0)
f2 = q * np.sin(np.pi * x[:, 0] / 2.0)
return np.vstack([f1, f2]).T
def T2(x):
x = np.atleast_2d(x)
# Sphere component in g
q = 1.0 + np.sum(x[:, 1:] ** 2, axis=1)
f1 = x[:, 0]
f2 = q * (1.0 - np.sqrt(x[:, 0] / q))
return np.vstack([f1, f2]).T
lb = np.full(dim, -80.0)
ub = np.full(dim, 80.0)
lb[0] = 0.0
ub[0] = 1.0
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb, upper_bound=ub)
problem.add_task(T2, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P8(self) -> MTOP:
"""
Generates Problem 8: **T1 (DTLZ1-like, 3-obj) vs T2 (ZDT2-like, 2-obj)**.
T1 is 3-objective, T2 is 2-objective. Both are 20-dimensional.
- T1: Modified DTLZ1-like with Rosenbrock component in :math:`g`. PF is a **plane** (linear).
- T2: Modified ZDT2-like with rotated Sphere component in :math:`g` (Mnm2). PF is continuous, non-convex.
- Relationship: Tasks have different number of objectives and different PF shapes/geometries.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
dim = 20
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Mnm2.mat')
mat_file = io.BytesIO(data_bytes)
Mnm2 = scipy.io.loadmat(mat_file)['Mnm2']
def T1(x):
x = np.atleast_2d(x)
part = x[:, 2:-1]
next_part = x[:, 3:]
# Rosenbrock component in g (DTLZ1-like)
q = 1.0 + np.sum(100 * (part ** 2 - next_part) ** 2 + (1.0 - part) ** 2, axis=1)
f1 = q * np.cos(np.pi * x[:, 0] / 2) * np.cos(np.pi * x[:, 1] / 2)
f2 = q * np.cos(np.pi * x[:, 0] / 2) * np.sin(np.pi * x[:, 1] / 2)
f3 = q * np.sin(np.pi * x[:, 0] / 2)
return np.vstack([f1, f2, f3]).T
def T2(x):
x = np.atleast_2d(x)
# Rotated Sphere component in g (ZDT2-like)
z = x[:, 2:] @ Mnm2.T
q = 1.0 + np.sum(z ** 2, axis=1)
s = 0.5 * np.sum(x[:, :2], axis=1)
f1 = s
f2 = q * (1.0 - (s / q) ** 2)
return np.vstack([f1, f2]).T
lb = np.full(dim, -20.0)
ub = np.full(dim, 20.0)
lb[:2] = 0.0
ub[:2] = 1.0
problem = MTOP()
problem.add_task(T1, dim=dim, lower_bound=lb, upper_bound=ub)
problem.add_task(T2, dim=dim, lower_bound=lb, upper_bound=ub)
return problem
[docs]
def P9(self) -> MTOP:
"""
Generates Problem 9: **T1 (DTLZ1-like, 3-obj) vs T2 (ZDT2-like, 2-obj)**.
T1 is 3-objective (25-dimensional), T2 is 2-objective (50-dimensional).
- T1: Modified DTLZ1-like with shifted Griewank component (Snl1) in :math:`g`. PF is a **plane** (linear).
- T2: Modified ZDT2-like with Ackley component in :math:`g`. PF is continuous, non-convex.
- Relationship: Different objectives, different dimensions, and different PF shapes.
Returns
-------
MTOP
A Multi-Task Multi-Objective Optimization Problem instance.
"""
data_bytes = pkgutil.get_data('ddmtolab.Problems.MTMO', f'{self.data_dir}/Snl1.mat')
mat_file = io.BytesIO(data_bytes)
Snl1 = scipy.io.loadmat(mat_file)['Snl1'].flatten()
dim1 = 25
dim2 = 50
def T1(x):
x = np.atleast_2d(x)
z = x[:, 2:25] - Snl1
a = np.arange(1, 24)
# Shifted Griewank component in g (DTLZ1-like)
q = 2.0 + (1.0 / 4000.0) * np.sum(z ** 2, axis=1) - np.prod(np.cos(z / np.sqrt(a)), axis=1)
f1 = q * np.cos(np.pi * x[:, 0] / 2) * np.cos(np.pi * x[:, 1] / 2)
f2 = q * np.cos(np.pi * x[:, 0] / 2) * np.sin(np.pi * x[:, 1] / 2)
f3 = q * np.sin(np.pi * x[:, 0] / 2)
return np.vstack([f1, f2, f3]).T
def T2(x):
x = np.atleast_2d(x)
r = x.shape[1]
# Ackley component in g (ZDT2-like)
q = 21.0 + np.e - 20.0 * np.exp(-0.2 * np.sqrt(np.sum(x[:, 2:] ** 2, axis=1) / (r - 2))) \
- np.exp(np.sum(np.cos(2.0 * np.pi * x[:, 2:]), axis=1) / (r - 2))
s = 0.5 * np.sum(x[:, :2], axis=1)
f1 = s
f2 = q * (1.0 - (s / q) ** 2)
return np.vstack([f1, f2]).T
lb1 = np.full(dim1, -50.0)
ub1 = np.full(dim1, 50.0)
lb1[:2] = 0.0
ub1[:2] = 1.0
lb2 = np.full(dim2, -100.0)
ub2 = np.full(dim2, 100.0)
lb2[:2] = 0.0
ub2[:2] = 1.0
problem = MTOP()
problem.add_task(T1, dim=dim1, lower_bound=lb1, upper_bound=ub1)
problem.add_task(T2, dim=dim2, lower_bound=lb2, upper_bound=ub2)
return problem
# --- True Pareto Front (PF) Functions ---
def P1_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P1, Task 1.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P1_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P1, Task 2.
The PF is parabolic-like (non-convex): :math:`f_2 = 1 - f_1^2`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
def P2_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P2, Task 1.
The PF is parabolic-like (non-convex): :math:`f_2 = 1 - f_1^2`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
def P2_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P2, Task 2.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P3_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P3, Task 1.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P3_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P3, Task 2.
The PF is inverse square root (convex): :math:`f_2 = 1 - \\sqrt{f_1}`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - np.sqrt(f1)
return np.column_stack([f1, f2])
def P4_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P4, Task 1.
The PF is inverse square root (convex): :math:`f_2 = 1 - \\sqrt{f_1}`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - np.sqrt(f1)
return np.column_stack([f1, f2])
def P4_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P4, Task 2.
The PF is inverse square root (convex): :math:`f_2 = 1 - \\sqrt{f_1}`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - np.sqrt(f1)
return np.column_stack([f1, f2])
def P5_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P5, Task 1.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P5_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P5, Task 2.
The PF is parabolic-like (non-convex): :math:`f_2 = 1 - f_1^2`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
def P6_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P6, Task 1.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P6_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P6, Task 2.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P7_T1_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P7, Task 1.
The PF is a quarter circle arc (non-convex).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
theta = np.linspace(0, np.pi / 2, N)
f1 = np.cos(theta)
f2 = np.sin(theta)
return np.column_stack([f1, f2])
def P7_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P7, Task 2.
The PF is inverse square root (convex): :math:`f_2 = 1 - \\sqrt{f_1}`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - np.sqrt(f1)
return np.column_stack([f1, f2])
def P8_T1_PF(N, M=3) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P8, Task 1 (3-objective).
The PF is a portion of a **sphere** (linear for DTLZ1-like).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 3).
Returns
-------
np.ndarray
Array of shape (N', 3) representing the PF points.
"""
n_sqrt = int(np.sqrt(N))
theta = np.linspace(0, np.pi / 2, n_sqrt)
phi = np.linspace(0, np.pi / 2, n_sqrt)
points = []
for t in theta:
for p in phi:
# DTLZ1-like PF: f1 + f2 + f3 = 1
f1 = np.sin(t) * np.cos(p)
f2 = np.sin(t) * np.sin(p)
f3 = np.cos(t)
points.append([f1, f2, f3])
return np.array(points)
def P8_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P8, Task 2 (2-objective).
The PF is parabolic-like (non-convex): :math:`f_2 = 1 - f_1^2`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
def P9_T1_PF(N, M=3) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P9, Task 1 (3-objective).
The PF is a portion of a **sphere** (linear for DTLZ1-like).
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 3).
Returns
-------
np.ndarray
Array of shape (N', 3) representing the PF points.
"""
n_sqrt = int(np.sqrt(N))
theta = np.linspace(0, np.pi / 2, n_sqrt)
phi = np.linspace(0, np.pi / 2, n_sqrt)
points = []
for t in theta:
for p in phi:
# DTLZ1-like PF: f1 + f2 + f3 = 1
f1 = np.sin(t) * np.cos(p)
f2 = np.sin(t) * np.sin(p)
f3 = np.cos(t)
points.append([f1, f2, f3])
return np.array(points)
def P9_T2_PF(N, M=2) -> np.ndarray:
"""
Computes the True Pareto Front (PF) for P9, Task 2 (2-objective).
The PF is parabolic-like (non-convex): :math:`f_2 = 1 - f_1^2`.
Parameters
----------
N : int
Number of points to generate on the PF.
M : int, optional
Number of objectives (default is 2).
Returns
-------
np.ndarray
Array of shape (N, 2) representing the PF points.
"""
f1 = np.linspace(0, 1, N)
f2 = 1 - f1 ** 2
return np.column_stack([f1, f2])
SETTINGS = {
'metric': 'IGD',
'n_pf': 1000,
'pf_path': './MOReference',
'P1': {'T1': P1_T1_PF, 'T2': P1_T2_PF},
'P2': {'T1': P2_T1_PF, 'T2': P2_T2_PF},
'P3': {'T1': P3_T1_PF, 'T2': P3_T2_PF},
'P4': {'T1': P4_T1_PF, 'T2': P4_T2_PF},
'P5': {'T1': P5_T1_PF, 'T2': P5_T2_PF},
'P6': {'T1': P6_T1_PF, 'T2': P6_T2_PF},
'P7': {'T1': P7_T1_PF, 'T2': P7_T2_PF},
'P8': {'T1': P8_T1_PF, 'T2': P8_T2_PF},
'P9': {'T1': P9_T1_PF, 'T2': P9_T2_PF},
}