"""CMT constrained multi-task benchmark suite.
9 two-task single-objective problems, each pairing two constrained landscapes.
Every task carries one inequality constraint, except the second task of CMT6 and
CMT8, which carries two. Tasks are ``D``-dimensional (50 by default).
The module also hosts the constrained function library the suite is built from:
each ``C_<Function><type>`` returns both the objective and the constraint
violation of one landscape under one of the constraint types.
"""
import numpy as np
from typing import Tuple
from ddmtolab.Methods.mtop import MTOP
# ==============================================================================
# Constrained Ackley Functions
# ==============================================================================
def C_Ackley1(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Ackley function with Type 1 constraint.
Constraint Type 1: Complex periodic constraint
g(x) = sum(x^2 - 5000*cos(0.1*pi*x) - 4000) <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Ackley objective
sum1 = np.sum(x ** 2, axis=1) / D
sum2 = np.sum(np.cos(2 * np.pi * x), axis=1) / D
Obj = -20 * np.exp(-0.2 * np.sqrt(sum1)) - np.exp(sum2) + 20 + np.e
# Compute constraint (Type 1: complex periodic)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
g = np.sum(x_con ** 2 - 5000 * np.cos(0.1 * np.pi * x_con) - 4000, axis=1)
g[g < 0] = 0
Con = g
return Obj, Con
def C_Ackley2(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Ackley function with Type 2 constraint.
Constraint Type 2: Spherical constraint
g(x) = sum(x^2) - 100*D <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Ackley objective
sum1 = np.sum(x ** 2, axis=1) / D
sum2 = np.sum(np.cos(2 * np.pi * x), axis=1) / D
Obj = -20 * np.exp(-0.2 * np.sqrt(sum1)) - np.exp(sum2) + 20 + np.e
# Compute constraint (Type 2: spherical)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
g = np.sum(x_con ** 2, axis=1) - 100 * D
g[g < 0] = 0
Con = g
return Obj, Con
# ==============================================================================
# Constrained Griewank Functions
# ==============================================================================
def C_Griewank1(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Griewank function with Type 1 constraint.
Constraint Type 1: Complex periodic constraint
g(x) = sum(x^2 - 5000*cos(0.1*pi*x) - 4000) <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Griewank objective
sum1 = np.sum(x ** 2, axis=1)
sum2 = np.ones(ps)
for i in range(D):
sum2 *= np.cos(x[:, i] / np.sqrt(i + 1))
Obj = 1 + sum1 / 4000 - sum2
# Compute constraint (Type 1: complex periodic)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = var - opt_con[:D]
g = np.sum(x_con ** 2 - 5000 * np.cos(0.1 * np.pi * x_con) - 4000, axis=1)
g[g < 0] = 0
Con = g
return Obj, Con
def C_Griewank2(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Griewank function with Type 2 constraint.
Constraint Type 2: Spherical constraint
g(x) = sum(x^2) - 100*D <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Griewank objective
sum1 = np.sum(x ** 2, axis=1)
sum2 = np.ones(ps)
for i in range(D):
sum2 *= np.cos(x[:, i] / np.sqrt(i + 1))
Obj = 1 + sum1 / 4000 - sum2
# Compute constraint (Type 2: spherical)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = var - opt_con[:D]
g = np.sum(x_con ** 2, axis=1) - 100 * D
g[g < 0] = 0
Con = g
return Obj, Con
# ==============================================================================
# Constrained Rastrigin Functions
# ==============================================================================
def C_Rastrigin1(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Rastrigin function with Type 1 constraint.
Constraint Type 1: Complex periodic constraint
g(x) = sum(x^2 - 5000*cos(0.1*pi*x) - 4000) <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Rastrigin objective
Obj = 10 * D + np.sum(x ** 2 - 10 * np.cos(2 * np.pi * x), axis=1)
# Compute constraint (Type 1: complex periodic)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
g = np.sum(x_con ** 2 - 5000 * np.cos(0.1 * np.pi * x_con) - 4000, axis=1)
g[g < 0] = 0
Con = g
return Obj, Con
def C_Rastrigin2(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Rastrigin function with Type 2 constraint.
Constraint Type 2: Spherical constraint
g(x) = sum(x^2) - 100*D <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Rastrigin objective
Obj = 10 * D + np.sum(x ** 2 - 10 * np.cos(2 * np.pi * x), axis=1)
# Compute constraint (Type 2: spherical)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
g = np.sum(x_con ** 2, axis=1) - 100 * D
g[g < 0] = 0
Con = g
return Obj, Con
def C_Rastrigin4(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Rastrigin function with Type 4 constraint (equality-like).
Constraint Type 4: Equality-like constraint
h(x) = |sum(-x * sin(0.1*pi*x))| - 1e-4 <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Rastrigin objective
Obj = 10 * D + np.sum(x ** 2 - 10 * np.cos(2 * np.pi * x), axis=1)
# Compute constraint (Type 4: equality-like)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
h = -np.sum(x_con * np.sin(0.1 * np.pi * x_con), axis=1)
h = np.abs(h) - 1e-4
h[h < 0] = 0
Con = h
return Obj, Con
# ==============================================================================
# Constrained Rosenbrock Functions
# ==============================================================================
def C_Rosenbrock1(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Rosenbrock function with Type 1 constraint.
Constraint Type 1: Complex periodic constraint
g(x) = sum(x^2 - 5000*cos(0.1*pi*x) - 4000) <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Rosenbrock objective
if D == 1:
Obj = 100 * (x[:, 0] - x[:, 0] ** 2) ** 2 + (x[:, 0] - 1) ** 2
else:
Obj = np.sum(100 * (x[:, 1:] - x[:, :-1] ** 2) ** 2 +
(x[:, :-1] - 1) ** 2, axis=1)
# Compute constraint (Type 1: complex periodic)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
g = np.sum(x_con ** 2 - 5000 * np.cos(0.1 * np.pi * x_con) - 4000, axis=1)
g[g < 0] = 0
Con = g
return Obj, Con
def C_Rosenbrock2(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Rosenbrock function with Type 2 constraint.
Constraint Type 2: Spherical constraint
g(x) = sum(x^2) - 100*D <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Rosenbrock objective
if D == 1:
Obj = 100 * (x[:, 0] - x[:, 0] ** 2) ** 2 + (x[:, 0] - 1) ** 2
else:
Obj = np.sum(100 * (x[:, 1:] - x[:, :-1] ** 2) ** 2 +
(x[:, :-1] - 1) ** 2, axis=1)
# Compute constraint (Type 2: spherical)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 2 * (var - opt_con[:D])
g = np.sum(x_con ** 2, axis=1) - 100 * D
g[g < 0] = 0
Con = g
return Obj, Con
# ==============================================================================
# Constrained Schwefel Functions
# ==============================================================================
def C_Schwefel1(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Schwefel function with Type 1 constraint.
Constraint Type 1: Complex periodic constraint (scaled by 0.2)
g(x) = sum(x^2 - 5000*cos(0.1*pi*x) - 4000) <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Schwefel objective
sum1 = np.sum(x * np.sin(np.sqrt(np.abs(x))), axis=1)
Obj = 418.9829 * D - sum1
# Compute constraint (Type 1: complex periodic, scaled by 0.2)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 0.2 * (var - opt_con[:D])
g = np.sum(x_con ** 2 - 5000 * np.cos(0.1 * np.pi * x_con) - 4000, axis=1)
g[g < 0] = 0
Con = g
return Obj, Con
def C_Schwefel2(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Schwefel function with Type 2 constraint.
Constraint Type 2: Spherical constraint (scaled by 0.2)
g(x) = sum(x^2) - 100*D <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Schwefel objective
sum1 = np.sum(x * np.sin(np.sqrt(np.abs(x))), axis=1)
Obj = 418.9829 * D - sum1
# Compute constraint (Type 2: spherical, scaled by 0.2)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 0.2 * (var - opt_con[:D])
g = np.sum(x_con ** 2, axis=1) - 100 * D
g[g < 0] = 0
Con = g
return Obj, Con
# ==============================================================================
# Constrained Sphere Function
# ==============================================================================
def C_Sphere1(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Sphere function with Type 1 constraint.
Constraint Type 1: Complex periodic constraint
g(x) = sum(x^2 - 5000*cos(0.1*pi*x) - 4000) <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples,).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Sphere objective
Obj = np.sum(x ** 2, axis=1)
# Compute constraint (Type 1: complex periodic)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = var - opt_con[:D]
g = np.sum(x_con ** 2 - 5000 * np.cos(0.1 * np.pi * x_con) - 4000, axis=1)
g[g < 0] = 0
Con = g
return Obj, Con
# ==============================================================================
# Constrained Weierstrass Function
# ==============================================================================
def C_Weierstrass3(var: np.ndarray, M: np.ndarray, opt: np.ndarray,
opt_con: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
Weierstrass function with Type 3 constraint (two constraints).
Constraint Type 3: Two constraints
g1(x) = -sum(|x|) + 12*D <= 0
g2(x) = sum(x^2) - 500*D <= 0
Parameters
----------
var : np.ndarray
Design variables, shape (n_samples, dim).
M : np.ndarray
Rotation matrix, shape (dim, dim) or (1, dim).
opt : np.ndarray
Shift vector for objective, shape (dim,) or (1, dim).
opt_con : np.ndarray
Shift vector for constraint, shape (dim,) or (1, dim).
Returns
-------
Obj : np.ndarray
Objective values, shape (n_samples,).
Con : np.ndarray
Constraint violations, shape (n_samples, 2).
"""
var = np.atleast_2d(var)
ps, D = var.shape
# Handle rotation matrix
if M.size == 1 or (M.ndim == 2 and M.shape[0] == 1):
M = np.eye(D) if M.size == 1 else float(M[0, 0]) * np.eye(D)
# Handle shift vectors
if opt.ndim == 1 or opt.shape[0] == 1:
opt = np.atleast_1d(opt).flatten()
if opt.size == 1:
opt = np.full(D, opt[0])
# Apply rotation and shift for objective
x = (M[:D, :D] @ (var - opt[:D]).T).T
# Compute Weierstrass objective
a = 0.5
b = 3
kmax = 20
Obj = np.zeros(ps)
for i in range(D):
for k in range(kmax + 1):
Obj += a ** k * np.cos(2 * np.pi * b ** k * (x[:, i] + 0.5))
for k in range(kmax + 1):
Obj -= D * a ** k * np.cos(2 * np.pi * b ** k * 0.5)
# Compute constraints (Type 3: two constraints, scaled by 200)
if opt_con.ndim == 1 or opt_con.shape[0] == 1:
opt_con = np.atleast_1d(opt_con).flatten()
if opt_con.size == 1:
opt_con = np.full(D, opt_con[0])
x_con = 200 * (var - opt_con[:D])
g1 = -np.sum(np.abs(x_con), axis=1) + 12 * D
g2 = np.sum(x_con ** 2, axis=1) - 500 * D
g1[g1 < 0] = 0
g2[g2 < 0] = 0
Con = np.column_stack([g1, g2])
return Obj, Con
[docs]
class CMT:
"""
CMT (Constrained Multi-Task) benchmark problems.
This class provides constrained multi-task optimization problems
with various function combinations and constraint types.
"""
problem_information = {
'n_cases': 9,
'n_tasks': '2',
'n_dims': 'D',
'n_objs': '1',
'n_cons': '[1, 2]',
'type': 'synthetic',
}
def __init__(self):
pass
[docs]
def CMT1(self, D: int = 50) -> MTOP:
"""
CMT Problem 1: Griewank (Type 1 constraint) + Rastrigin (Type 1 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = np.zeros(D)
opt_con1 = -40 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = 20 * np.ones(D)
def task1_func(x):
obj, con = C_Griewank1(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Rastrigin1(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-100,
upper_bound=100
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-50,
upper_bound=50
)
return problem
[docs]
def CMT2(self, D: int = 50) -> MTOP:
"""
CMT Problem 2: Ackley (Type 2 constraint) + Rastrigin (Type 2 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = np.zeros(D)
opt_con1 = -4 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = 4 * np.ones(D)
def task1_func(x):
obj, con = C_Ackley2(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Rastrigin2(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-50,
upper_bound=50
)
return problem
[docs]
def CMT3(self, D: int = 50) -> MTOP:
"""
CMT Problem 3: Ackley (Type 2 constraint) + Schwefel (Type 1 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = 42.096 * np.ones(D)
opt_con1 = 40 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = 400 * np.ones(D)
def task1_func(x):
obj, con = C_Ackley2(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Schwefel1(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-500,
upper_bound=500
)
return problem
[docs]
def CMT4(self, D: int = 50) -> MTOP:
"""
CMT Problem 4: Rastrigin (Type 1 constraint) + Sphere (Type 1 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = np.zeros(D)
opt_con1 = -20 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = 30 * np.ones(D)
def task1_func(x):
obj, con = C_Rastrigin1(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Sphere1(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-100,
upper_bound=100
)
return problem
[docs]
def CMT5(self, D: int = 50) -> MTOP:
"""
CMT Problem 5: Ackley (Type 1 constraint) + Rosenbrock (Type 2 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = np.zeros(D)
opt_con1 = -30 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = np.zeros(D)
def task1_func(x):
obj, con = C_Ackley1(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Rosenbrock2(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-50,
upper_bound=50
)
return problem
[docs]
def CMT6(self, D: int = 50) -> MTOP:
"""
CMT Problem 6: Ackley (Type 2 constraint) + Weierstrass (Type 3 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = 2 * np.ones(D)
opt_con1 = np.zeros(D)
M2 = np.array([[1.0]])
opt2 = 0.1 * np.ones(D)
opt_con2 = np.zeros(D)
def task1_func(x):
obj, con = C_Ackley2(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Weierstrass3(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-0.5,
upper_bound=0.5
)
return problem
[docs]
def CMT7(self, D: int = 50) -> MTOP:
"""
CMT Problem 7: Rosenbrock (Type 1 constraint) + Rastrigin (Type 1 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = -30 * np.ones(D)
opt_con1 = -35 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = 35 * np.ones(D)
opt_con2 = 40 * np.ones(D)
def task1_func(x):
obj, con = C_Rosenbrock1(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Rastrigin1(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-50,
upper_bound=50
)
return problem
[docs]
def CMT8(self, D: int = 50) -> MTOP:
"""
CMT Problem 8: Griewank (Type 2 constraint) + Weierstrass (Type 3 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = np.zeros(D)
opt_con1 = -30 * np.ones(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = 0.2 * np.ones(D)
def task1_func(x):
obj, con = C_Griewank2(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Weierstrass3(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-100,
upper_bound=100
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-0.5,
upper_bound=0.5
)
return problem
[docs]
def CMT9(self, D: int = 50) -> MTOP:
"""
CMT Problem 9: Rastrigin (Type 4 constraint) + Schwefel (Type 2 constraint).
Parameters
----------
D : int, optional
Number of decision variables (default is 50).
Returns
-------
MTOP
A Multi-Task Optimization Problem instance.
"""
M1 = np.array([[1.0]])
opt1 = -10 * np.ones(D)
opt_con1 = np.zeros(D)
M2 = np.array([[1.0]])
opt2 = np.zeros(D)
opt_con2 = 100 * np.ones(D)
def task1_func(x):
obj, con = C_Rastrigin4(x, M1, opt1, opt_con1)
return obj, con
def task2_func(x):
obj, con = C_Schwefel2(x, M2, opt2, opt_con2)
return obj, con
def task1_objective(x):
obj, _ = task1_func(x)
return obj
def task1_constraint(x):
_, con = task1_func(x)
return con
def task2_objective(x):
obj, _ = task2_func(x)
return obj
def task2_constraint(x):
_, con = task2_func(x)
return con
problem = MTOP()
problem.add_task(
objective_func=task1_objective,
dim=D,
constraint_func=task1_constraint,
lower_bound=-50,
upper_bound=50
)
problem.add_task(
objective_func=task2_objective,
dim=D,
constraint_func=task2_constraint,
lower_bound=-500,
upper_bound=500
)
return problem