Source code for ddmtolab.Problems.MTSO.stop

import scipy.io
from ddmtolab.Problems.BasicFunctions.basic_functions import *
from ddmtolab.Methods.mtop import MTOP
import numpy as np
import pkgutil
import io


[docs] class STOP: """ Implementation of STOP (Scalable Test Problem Generator for Sequential Transfer Optimization) benchmark problems. These problems are designed to challenge algorithms with many tasks derived from various benchmark functions with different characteristics, testing transfer learning across many similar but distinct tasks. Reference: X. Xue et al., "A Scalable Test Problem Generator for Sequential Transfer Optimization," IEEE Trans. Cybern., vol. 55, no. 5, pp. 2110-2123, 2025. Attributes ---------- data_dir : str The directory path for problem data files. """ problem_information = { 'n_cases': 12, 'n_tasks': 'K', 'n_dims': '[25, 50]', 'n_objs': '1', 'n_cons': '0', 'type': 'synthetic', } def __init__(self): self.data_dir = 'data_stop' @staticmethod def _get_function_and_bounds(func_name, dim): """ Get the objective function and bounds based on function name. Parameters ---------- func_name : str Name of the function (e.g., 'Sphere', 'Ellipsoid', etc.) dim : int Dimension of the problem Returns ------- tuple (function, lower_bound, upper_bound) """ func_name = func_name.strip() if func_name == 'Sphere': func = lambda x: Sphere(x, np.eye(dim), np.zeros(dim), 0) lb = np.full(dim, -100.0) ub = np.full(dim, 100.0) elif func_name == 'Ellipsoid': func = lambda x: STOP.S_Ellipsoid(x, np.zeros(dim)) lb = np.full(dim, -50.0) ub = np.full(dim, 50.0) elif func_name == 'Schwefel': func = lambda x: STOP.S_Schwefel(x, np.zeros(dim)) lb = np.full(dim, -30.0) ub = np.full(dim, 30.0) elif func_name == 'Quartic': func = lambda x: STOP.S_Quartic(x, np.zeros(dim)) lb = np.full(dim, -5.0) ub = np.full(dim, 5.0) elif func_name == 'Ackley': func = lambda x: Ackley(x, np.eye(dim), np.zeros(dim), 0) lb = np.full(dim, -32.0) ub = np.full(dim, 32.0) elif func_name == 'Rastrigin': func = lambda x: Rastrigin(x, np.eye(dim), np.zeros(dim), 0) lb = np.full(dim, -10.0) ub = np.full(dim, 10.0) elif func_name == 'Griewank': func = lambda x: Griewank(x, np.eye(dim), np.zeros(dim), 0) lb = np.full(dim, -200.0) ub = np.full(dim, 200.0) elif func_name == 'Levy': func = lambda x: STOP.S_Levy(x, np.zeros(dim)) lb = np.full(dim, -20.0) ub = np.full(dim, 20.0) else: raise ValueError(f"Unknown function name: {func_name}") return func, lb, ub def _create_task_function(self, func_name, dim, opt): """ Create task function based on function name. Parameters ---------- func_name : str Name of the function dim : int Dimension opt : np.ndarray Optimal point (shift vector) Returns ------- callable Task function """ func_name = func_name.strip() if func_name == 'Levy': return lambda x, o=opt: self.S_Levy(x, o) elif func_name == 'Ackley': return lambda x, o=opt: Ackley(x, np.eye(dim), o, 0) elif func_name == 'Schwefel': return lambda x, o=opt: self.S_Schwefel(x, o) elif func_name == 'Quartic': return lambda x, o=opt: self.S_Quartic(x, o) elif func_name == 'Rastrigin': return lambda x, o=opt: Rastrigin(x, np.eye(dim), o, 0) elif func_name == 'Griewank': return lambda x, o=opt: Griewank(x, np.eye(dim), o, 0) elif func_name == 'Ellipsoid': return lambda x, o=opt: self.S_Ellipsoid(x, o) elif func_name == 'Sphere': return lambda x, o=opt: Sphere(x, np.eye(dim), o, 0) else: raise ValueError(f"Unknown function: {func_name}") def _load_and_create_problem(self, mat_filename, dim, K): """ Generic method to load MAT file and create MTOP problem. Parameters ---------- mat_filename : str MAT file name dim : int Problem dimension K : int Number of tasks Returns ------- MTOP Multi-Task Optimization Problem """ data_bytes = pkgutil.get_data('ddmtolab.Problems.MTSO', f'{self.data_dir}/{mat_filename}') mat_file = io.BytesIO(data_bytes) mat_data = scipy.io.loadmat(mat_file) target = mat_data['target'] sources = mat_data['sources'] if K > 1 else None problem = MTOP() # Add target task target_opt = target[0, 0]['x_best'].flatten() target_name = target[0, 0]['name'][0] _, lb, ub = self._get_function_and_bounds(target_name, dim) task_function = self._create_task_function(target_name, dim, target_opt) problem.add_task(task_function, dim=dim, lower_bound=lb, upper_bound=ub) # Add source tasks if K > 1: for i in range(K - 1): source_opt = sources[0, i]['x_best'].flatten() source_name = sources[0, i]['name'][0] _, lb, ub = self._get_function_and_bounds(source_name, dim) task_function = self._create_task_function(source_name, dim, source_opt) problem.add_task(task_function, dim=dim, lower_bound=lb, upper_bound=ub) return problem
[docs] def STOP1(self, K=10) -> MTOP: """STOP Problem 1: Sphere-Ta-hh2-d50-k49""" return self._load_and_create_problem('Sphere-Ta-hh2-d50-k49.mat', 50, K)
[docs] def STOP2(self, K=10) -> MTOP: """STOP Problem 2: Ellipsoid-Te-hh2-d25-k49""" return self._load_and_create_problem('Ellipsoid-Te-hh2-d25-k49.mat', 25, K)
[docs] def STOP3(self, K=10) -> MTOP: """STOP Problem 3: Schwefel-Ta-hh2-d30-k49""" return self._load_and_create_problem('Schwefel-Ta-hh2-d30-k49.mat', 30, K)
[docs] def STOP4(self, K=10) -> MTOP: """STOP Problem 4: Quartic-Te-hh2-d50-k49""" return self._load_and_create_problem('Quartic-Te-hh2-d50-k49.mat', 50, K)
[docs] def STOP5(self, K=10) -> MTOP: """STOP Problem 5: Ackley-Ta-hm1-d25-k49""" return self._load_and_create_problem('Ackley-Ta-hm1-d25-k49.mat', 25, K)
[docs] def STOP6(self, K=10) -> MTOP: """STOP Problem 6: Rastrigin-Te-hm2-d50-k49""" return self._load_and_create_problem('Rastrigin-Te-hm2-d50-k49.mat', 50, K)
[docs] def STOP7(self, K=10) -> MTOP: """STOP Problem 7: Griewank-Ta-hm3-d25-k49""" return self._load_and_create_problem('Griewank-Ta-hm3-d25-k49.mat', 25, K)
[docs] def STOP8(self, K=10) -> MTOP: """STOP Problem 8: Levy-Te-hm4-d30-k49""" return self._load_and_create_problem('Levy-Te-hm4-d30-k49.mat', 30, K)
[docs] def STOP9(self, K=10) -> MTOP: """STOP Problem 9: Sphere-Ta-hl1-d25-k49""" return self._load_and_create_problem('Sphere-Ta-hl1-d25-k49.mat', 25, K)
[docs] def STOP10(self, K=10) -> MTOP: """STOP Problem 10: Rastrigin-Te-hl2-d30-k49""" return self._load_and_create_problem('Rastrigin-Te-hl2-d30-k49.mat', 30, K)
[docs] def STOP11(self, K=10) -> MTOP: """STOP Problem 11: Ackley-Ta-hl2-d50-k49""" return self._load_and_create_problem('Ackley-Ta-hl2-d50-k49.mat', 50, K)
[docs] def STOP12(self, K=10) -> MTOP: """STOP Problem 12: Ellipsoid-Te-hl1-d50-k49""" return self._load_and_create_problem('Ellipsoid-Te-hl1-d50-k49.mat', 50, K)
[docs] @staticmethod def S_Ellipsoid(var, opt): """Ellipsoid function (STOP variant, shifted only).""" if var.ndim != 2: raise ValueError("Input 'var' must be a 2D array: (n_samples, n_features)") ps, D = var.shape w = np.arange(D, 0, -1) diff = var - opt Obj = np.sum((diff ** 2) * w, axis=1) return Obj.reshape(-1, 1)
[docs] @staticmethod def S_Levy(var, opt): """Levy function (STOP variant, shifted only).""" if var.ndim != 2: raise ValueError("Input 'var' must be a 2D array: (n_samples, n_features)") ps, D = var.shape w = 1 + (var - opt) / 4 term1 = np.sin(np.pi * w[:, 0]) ** 2 wd = w[:, -1] term3 = (wd - 1) ** 2 * (1 + np.sin(2 * np.pi * wd) ** 2) if D > 1: wi = w[:, :-1] term2 = np.sum((wi - 1) ** 2 * (1 + 10 * np.sin(np.pi * wi + 1) ** 2), axis=1) else: term2 = 0 Obj = term1 + term2 + term3 return Obj.reshape(-1, 1)
[docs] @staticmethod def S_Quartic(var, opt): """Quartic function with noise (STOP variant, shifted only).""" if var.ndim != 2: raise ValueError("Input 'var' must be a 2D array: (n_samples, n_features)") ps, D = var.shape w = np.arange(1, D + 1) diff = var - opt Obj = np.sum((diff ** 4) * w, axis=1) + np.random.rand(ps) return Obj.reshape(-1, 1)
[docs] @staticmethod def S_Schwefel(var, opt): """Schwefel 2.2 function (STOP variant, shifted only).""" if var.ndim != 2: raise ValueError("Input 'var' must be a 2D array: (n_samples, n_features)") diff = var - opt abs_diff = np.abs(diff) Obj = np.sum(abs_diff, axis=1) + np.prod(abs_diff, axis=1) return Obj.reshape(-1, 1)