"""
CMT Constrained Function Library
==================================
This module contains all the constrained benchmark functions used in CMT problems.
Each function has both objective and constraint components.
"""
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',
'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