From 92ef8354223e77841407de26933c561311e80071 Mon Sep 17 00:00:00 2001 From: deastern Date: Tue, 15 Apr 2025 18:32:09 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E4=B8=AA=E5=8F=AF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E7=9B=AE=E6=A0=87=E8=BF=9B=E5=8C=96=E7=AD=96?= =?UTF-8?q?=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lead/config/settings.py | 30 +++- .../core/evolution_algorithms/__init__.py | 3 +- .../evolution_algorithms/multi_objective.py | 134 +++++++++++++++++ src/lead/core/multi_objective/evolution.py | 140 +++++------------- src/lead/core/operators/crossover_analysis.py | 19 ++- .../operators/multi_objective_crossover.py | 7 +- .../operators/multi_objective_mutation.py | 10 +- src/lead/core/operators/mutation_analysis.py | 50 +++++-- src/lead/problems/sort/evaluation.py | 31 +++- .../sort/multi_objective_evolution.log | 2 + .../20250415_180647/generation_0.json | 67 +++++++++ .../20250415_180647/generation_1.json | 67 +++++++++ src/lead/problems/sort/problem_config.json | 15 +- src/lead/scripts/run_evolution.py | 19 ++- 14 files changed, 445 insertions(+), 149 deletions(-) create mode 100644 src/lead/core/evolution_algorithms/multi_objective.py create mode 100644 src/lead/problems/sort/multi_objective_evolution.log create mode 100644 src/lead/problems/sort/multi_objective_history/20250415_180647/generation_0.json create mode 100644 src/lead/problems/sort/multi_objective_history/20250415_180647/generation_1.json diff --git a/src/lead/config/settings.py b/src/lead/config/settings.py index 099c97c..1eddf36 100644 --- a/src/lead/config/settings.py +++ b/src/lead/config/settings.py @@ -1,4 +1,5 @@ import os +from typing import Dict, Any, List # 项目根目录 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) @@ -16,10 +17,35 @@ DEFAULT_LLM_CONFIG = { "max_tokens": 1000 } +# 默认问题配置模板 +DEFAULT_PROBLEM_CONFIG = { + "description": "问题描述", + "function_name": "solution", + "input_format": "输入格式描述", + "output_format": "输出格式描述", + "evaluation_timeout": 30, + "multi_objective": False, # 是否使用多目标优化 + "objective_names": ["f1", "f2"], # 多目标名称列表 + "llm_config": DEFAULT_LLM_CONFIG, + "evolution_params": { + "algorithm": "TU", # TU: Tournament, DE: Differential Evolution, MO: Multi-Objective + "population_size": 8, + "generations": 10, + "mutation_rate": 0.3, + "crossover_rate": 0.5, + "tournament_size": 3, + "F": 0.5, # DE参数 + "CR": 0.7 # DE参数 + } +} + # 默认进化参数 DEFAULT_EVOLUTION_PARAMS = { "population_size": 8, "generations": 10, "mutation_rate": 0.3, - "crossover_rate": 0.5 -} \ No newline at end of file + "crossover_rate": 0.5, + "tournament_size": 3, + "F": 0.5, # DE参数 + "CR": 0.7 # DE参数 +} \ No newline at end of file diff --git a/src/lead/core/evolution_algorithms/__init__.py b/src/lead/core/evolution_algorithms/__init__.py index c3114bc..5c01d10 100644 --- a/src/lead/core/evolution_algorithms/__init__.py +++ b/src/lead/core/evolution_algorithms/__init__.py @@ -1,4 +1,5 @@ from .tournament import TournamentEvolution from .base import BaseEvolutionAlgorithm +from .multi_objective import MultiObjectiveEvolution -__all__ = ['TournamentEvolution', 'BaseEvolutionAlgorithm'] \ No newline at end of file +__all__ = ['TournamentEvolution', 'BaseEvolutionAlgorithm', 'MultiObjectiveEvolution'] \ No newline at end of file diff --git a/src/lead/core/evolution_algorithms/multi_objective.py b/src/lead/core/evolution_algorithms/multi_objective.py new file mode 100644 index 0000000..52f9bdb --- /dev/null +++ b/src/lead/core/evolution_algorithms/multi_objective.py @@ -0,0 +1,134 @@ +from typing import List +import random +import numpy as np +from .base import BaseEvolutionAlgorithm +from ..multi_objective.individual import MultiObjectiveIndividual +from ..llm_integration import LLMClient +from ..operators.multi_objective_crossover import MultiObjectiveCrossoverOperator +from ..operators.multi_objective_mutation import MultiObjectiveMutationOperator +from ..operators.verify_operator import VerifyOperator + +class MultiObjectiveEvolution(BaseEvolutionAlgorithm): + """多目标进化算法,基于NSGA-II框架""" + + def __init__(self, config: dict, llm_client: LLMClient): + super().__init__(config) + self.llm_client = llm_client + self.verify_operator = VerifyOperator(llm_client) + self.crossover_operator = MultiObjectiveCrossoverOperator( + llm_client, self.verify_operator) + self.mutation_operator = MultiObjectiveMutationOperator( + llm_client, self.verify_operator) + self.tournament_size = config.get("tournament_size", 3) + + def select(self, population: List[MultiObjectiveIndividual], + num_parents: int) -> List[MultiObjectiveIndividual]: + """基于非支配排序和拥挤距离的锦标赛选择""" + # 先进行非支配排序 + fronts = self._non_dominated_sort(population) + + # 计算拥挤距离 + for front in fronts: + for ind in front: + ind.crowding_distance(front) + + # 锦标赛选择 + parents = [] + for _ in range(num_parents): + candidates = random.sample(population, min(self.tournament_size, len(population))) + # 优先选择前沿等级高的,其次选择拥挤距离大的 + winner = min(candidates, key=lambda x: ( + x.rank, + -x.crowding_distance(fronts[x.rank]) + )) + parents.append(winner) + return parents + + def crossover(self, parents: List[MultiObjectiveIndividual]) -> List[MultiObjectiveIndividual]: + """多目标交叉操作""" + if len(parents) < 2: + return parents + + if random.random() < self.config.get("crossover_rate", 0.9): + # 获取目标名称(从第一个父代中获取) + objective_names = list(parents[0].fitnesses.keys()) + + # 执行多目标交叉 + child_code, _ = self.crossover_operator.crossover( + parents[0], parents[1], objective_names) + + if child_code: + return [MultiObjectiveIndividual( + child_code, + generation=parents[0].generation + 1 + )] + return [parents[0]] + + def mutate(self, individual: MultiObjectiveIndividual) -> MultiObjectiveIndividual: + """多目标变异操作""" + if random.random() < self.config.get("mutation_rate", 0.1): + # 获取目标名称 + objective_names = list(individual.fitnesses.keys()) + + # 执行多目标变异 + mutated_code, _ = self.mutation_operator.mutate( + individual, objective_names) + + if mutated_code: + return MultiObjectiveIndividual( + mutated_code, + generation=individual.generation + ) + return individual + + def survive(self, population: List[MultiObjectiveIndividual], + offspring: List[MultiObjectiveIndividual], + pop_size: int) -> List[MultiObjectiveIndividual]: + """基于非支配排序和拥挤距离的精英保留策略""" + combined_pop = population + offspring + + # 非支配排序 + fronts = self._non_dominated_sort(combined_pop) + + # 构建新种群 + new_pop = [] + for front in fronts: + if len(new_pop) + len(front) <= pop_size: + new_pop.extend(front) + else: + # 计算拥挤距离并选择 + for ind in front: + ind.crowding_distance(front) + front = sorted(front, key=lambda x: x.crowding_distance(front), reverse=True) + new_pop.extend(front[:pop_size - len(new_pop)]) + break + return new_pop[:pop_size] + + def _non_dominated_sort(self, population: List[MultiObjectiveIndividual]) -> List[List[MultiObjectiveIndividual]]: + """非支配排序""" + fronts = [[]] + for ind in population: + ind.domination_count = 0 + ind.dominated_set = [] + for other in population: + if ind.dominates(other): + ind.dominated_set.append(other) + elif other.dominates(ind): + ind.domination_count += 1 + if ind.domination_count == 0: + fronts[0].append(ind) + ind.rank = 0 + + i = 0 + while fronts[i]: + next_front = [] + for ind in fronts[i]: + for dominated_ind in ind.dominated_set: + dominated_ind.domination_count -= 1 + if dominated_ind.domination_count == 0: + dominated_ind.rank = i + 1 + next_front.append(dominated_ind) + i += 1 + fronts.append(next_front) + + return fronts[:-1] # 最后一个是空的 \ No newline at end of file diff --git a/src/lead/core/multi_objective/evolution.py b/src/lead/core/multi_objective/evolution.py index 95acecb..2c696e7 100644 --- a/src/lead/core/multi_objective/evolution.py +++ b/src/lead/core/multi_objective/evolution.py @@ -1,12 +1,13 @@ import os import json -import numpy as np -from typing import List, Dict, Any +from typing import List from ...utils.multi_objective.evaluator import MultiObjectiveEvaluator from ...utils.multi_objective.storage import MultiObjectiveGenerationStorage from .individual import MultiObjectiveIndividual from ..llm_integration import LLMClient from ...config.settings import DEFAULT_EVOLUTION_PARAMS +from ..evolution_algorithms.multi_objective import MultiObjectiveEvolution +from ..operators.initialize_operator import InitializeOperator class MultiObjectiveEvolutionEngine: """多目标进化引擎""" @@ -16,6 +17,7 @@ class MultiObjectiveEvolutionEngine: self.storage = MultiObjectiveGenerationStorage(problem_path) self.evaluator = MultiObjectiveEvaluator(problem_path) self.llm_client = LLMClient.from_config(problem_path) + self.initialize_operator = InitializeOperator(self.llm_client) # 加载进化参数 config = self._load_problem_config() @@ -23,16 +25,30 @@ class MultiObjectiveEvolutionEngine: **DEFAULT_EVOLUTION_PARAMS, **config.get("evolution_params", {}) } + + # 初始化多目标进化算法 + self.evolution_algorithm = MultiObjectiveEvolution( + self.evolution_params, + self.llm_client + ) + print(f"多目标进化参数:{self.evolution_params}") def initialize_population(self, size: int) -> List[MultiObjectiveIndividual]: - """初始化种群""" - # 这里可以集成LLM生成初始代码 + """使用LLM生成初始种群""" + problem_config = self._load_problem_config() population = [] - for _ in range(size): - # 示例代码,实际应使用LLM生成 - code = "def solution(input):\n return {'f1': 0.0, 'f2': 0.0}" - population.append(MultiObjectiveIndividual(code, generation=0)) + + while len(population) < size: + code = self.initialize_operator.generate_initial_code( + problem_config["description"], + problem_config["function_name"], + problem_config["input_format"], + problem_config["output_format"] + ) + if code: + population.append(MultiObjectiveIndividual(code, generation=0)) + return population def run_evolution(self, generations: int = None, population_size: int = None): @@ -54,18 +70,19 @@ class MultiObjectiveEvolutionEngine: print(f"\n开始第 {gen+1}/{generations} 代进化") # 生成子代 - offspring = self._generate_offspring(population) + offspring = [] + while len(offspring) < population_size: + parents = self.evolution_algorithm.select(population, 2) + children = self.evolution_algorithm.crossover(parents) + for child in children: + mutated_child = self.evolution_algorithm.mutate(child) + if mutated_child.code: + mutated_child.fitnesses = self.evaluator.evaluate(mutated_child.code) + print(f"新个体适应度:{mutated_child.fitnesses}") + offspring.append(mutated_child) - # 评估子代 - for ind in offspring: - ind.fitnesses = self.evaluator.evaluate(ind.code) - print(f"新个体适应度:{ind.fitnesses}") - - # 合并父代和子代 - combined_pop = population + offspring - - # 非支配排序和选择 - population = self._select_new_population(combined_pop, population_size) + # 生存选择 + population = self.evolution_algorithm.survive(population, offspring, population_size) # 保存当前代信息 self.storage.save_generation(gen, population) @@ -77,81 +94,6 @@ class MultiObjectiveEvolutionEngine: print("\n多目标进化完成!") return population - def _generate_offspring(self, population: List[MultiObjectiveIndividual]) -> List[MultiObjectiveIndividual]: - """生成子代个体""" - offspring = [] - for _ in range(len(population) // 2): - # 选择父代 - parents = self._select_parents(population) - - # 交叉 (这里可以集成LLM) - child_code = self._crossover(parents[0].code, parents[1].code) - - # 变异 (这里可以集成LLM) - mutated_code = self._mutate(child_code) - - offspring.append(MultiObjectiveIndividual(mutated_code, generation=parents[0].generation + 1)) - return offspring - - def _select_parents(self, population: List[MultiObjectiveIndividual]) -> List[MultiObjectiveIndividual]: - """使用锦标赛选择父代""" - tournament_size = min(5, len(population)) - parents = [] - for _ in range(2): - candidates = np.random.choice(population, tournament_size, replace=False) - # 基于非支配排序和拥挤距离选择 - candidates = sorted(candidates, key=lambda x: ( - -x.rank, # 优先选择前沿等级高的 - x.crowding_distance(population) # 其次选择拥挤距离大的 - )) - parents.append(candidates[0]) - return parents - - def _select_new_population(self, combined_pop: List[MultiObjectiveIndividual], size: int) -> List[MultiObjectiveIndividual]: - """选择新一代种群""" - fronts = self._non_dominated_sort(combined_pop) - new_pop = [] - for front in fronts: - if len(new_pop) + len(front) <= size: - new_pop.extend(front) - else: - # 计算拥挤距离并选择 - for ind in front: - ind.crowding_distance(front) - front = sorted(front, key=lambda x: x.crowding_distance(front), reverse=True) - new_pop.extend(front[:size - len(new_pop)]) - break - return new_pop - - def _non_dominated_sort(self, population: List[MultiObjectiveIndividual]) -> List[List[MultiObjectiveIndividual]]: - """非支配排序""" - fronts = [[]] - for ind in population: - ind.domination_count = 0 - ind.dominated_set = [] - for other in population: - if ind.dominates(other): - ind.dominated_set.append(other) - elif other.dominates(ind): - ind.domination_count += 1 - if ind.domination_count == 0: - fronts[0].append(ind) - ind.rank = 0 - - i = 0 - while fronts[i]: - next_front = [] - for ind in fronts[i]: - for dominated_ind in ind.dominated_set: - dominated_ind.domination_count -= 1 - if dominated_ind.domination_count == 0: - dominated_ind.rank = i + 1 - next_front.append(dominated_ind) - i += 1 - fronts.append(next_front) - - return fronts[:-1] # 最后一个是空的 - def _calculate_pareto_front(self, population: List[MultiObjectiveIndividual]) -> List[MultiObjectiveIndividual]: """计算Pareto前沿""" front = [] @@ -165,16 +107,6 @@ class MultiObjectiveEvolutionEngine: front.append(ind) return front - def _crossover(self, code1: str, code2: str) -> str: - """交叉操作""" - # 这里可以集成LLM的交叉操作 - return code1 # 简化示例 - - def _mutate(self, code: str) -> str: - """变异操作""" - # 这里可以集成LLM的变异操作 - return code # 简化示例 - def _load_problem_config(self) -> dict: """加载问题配置""" config_path = os.path.join(self.problem_path, "problem_config.json") diff --git a/src/lead/core/operators/crossover_analysis.py b/src/lead/core/operators/crossover_analysis.py index a2e0c24..949621a 100644 --- a/src/lead/core/operators/crossover_analysis.py +++ b/src/lead/core/operators/crossover_analysis.py @@ -4,7 +4,7 @@ from lead.core.multi_objective.individual import MultiObjectiveIndividual class CrossoverAnalysisOperator: """交叉分析算子:分析两个父代个体的目标值和支配关系""" - def analyze(self, parent1: MultiObjectiveIndividual, + def analyze(self, parent1: MultiObjectiveIndividual, parent2: MultiObjectiveIndividual, objective_names: List[str]) -> Dict: """ @@ -14,14 +14,16 @@ class CrossoverAnalysisOperator: "dominance": "parent1"|"parent2"|"non_dominated", "target_objective": str, # 主要优化目标 "parent1_strengths": List[str], # parent1优势目标 - "parent2_strengths": List[str] # parent2优势目标 + "parent2_strengths": List[str], # parent2优势目标 + "function_name": str # 函数名 } """ result = { "dominance": self._check_dominance(parent1, parent2), "target_objective": None, "parent1_strengths": [], - "parent2_strengths": [] + "parent2_strengths": [], + "function_name": self._extract_function_name(parent1.code) } # 比较各目标值,找出各自的优势目标 @@ -51,11 +53,18 @@ class CrossoverAnalysisOperator: return result - def _check_dominance(self, ind1: MultiObjectiveIndividual, + def _check_dominance(self, ind1: MultiObjectiveIndividual, ind2: MultiObjectiveIndividual) -> str: """检查支配关系""" if ind1.dominates(ind2): return "parent1" elif ind2.dominates(ind1): return "parent2" - return "non_dominated" \ No newline at end of file + return "non_dominated" + + def _extract_function_name(self, code: str) -> str: + """从代码中提取函数名""" + for line in code.split('\n'): + if line.strip().startswith('def '): + return line.split()[1].split('(')[0] + return "solution" # 默认函数名 \ No newline at end of file diff --git a/src/lead/core/operators/multi_objective_crossover.py b/src/lead/core/operators/multi_objective_crossover.py index df2c681..b362af6 100644 --- a/src/lead/core/operators/multi_objective_crossover.py +++ b/src/lead/core/operators/multi_objective_crossover.py @@ -32,11 +32,11 @@ class MultiObjectiveCrossoverOperator: new_code = self.llm_client._call_llm(prompt, operator="crossover") # 4. 验证代码 - verified_code = self._verify_code(new_code) + verified_code = self._verify_code(new_code, analysis_info["function_name"]) return verified_code, analysis_info - def _build_multi_obj_prompt(self, + def _build_multi_obj_prompt(self, parent1: MultiObjectiveIndividual, parent2: MultiObjectiveIndividual, analysis_info: Dict) -> str: @@ -49,7 +49,7 @@ class MultiObjectiveCrossoverOperator: 父代1代码: {parent1.code} - 父代2优势: {', '.join(analysis_info["parent2_strengths"])} + 父代2优势: {', '.join(analysis_info["parent2_strengths"])} 父代2代码: {parent2.code} @@ -57,6 +57,7 @@ class MultiObjectiveCrossoverOperator: 1. 保留两个父代在各自优势目标上的优点 2. 特别优化目标 '{target_obj}' 的性能 3. 确保代码完整且符合问题要求 + 4. 函数名必须为: {analysis_info["function_name"]} 只返回最终的Python代码,不要包含任何解释。 """ diff --git a/src/lead/core/operators/multi_objective_mutation.py b/src/lead/core/operators/multi_objective_mutation.py index 6291a94..f5d90a9 100644 --- a/src/lead/core/operators/multi_objective_mutation.py +++ b/src/lead/core/operators/multi_objective_mutation.py @@ -24,6 +24,10 @@ class MultiObjectiveMutationOperator: analysis_info = self.analysis_operator.analyze( individual, objective_names, reference_front) + # 确保target_objective有效 + if not analysis_info["target_objective"]: + analysis_info["target_objective"] = objective_names[0] if objective_names else "error" + # 2. 构建多目标变异提示词 prompt = self._build_multi_obj_prompt( individual, analysis_info) @@ -33,7 +37,7 @@ class MultiObjectiveMutationOperator: # 4. 验证代码 verified_code = self._verify_code( - new_code, analysis_info["target_objective"]) + new_code, analysis_info["function_name"]) return verified_code, analysis_info @@ -42,7 +46,7 @@ class MultiObjectiveMutationOperator: analysis_info: Dict) -> str: """构建多目标变异提示词""" target_obj = analysis_info["target_objective"] - improvement = analysis_info["improvement_potential"][target_obj] + improvement = analysis_info["improvement_potential"].get(target_obj, 1.0) return f""" 请对以下算法进行多目标优化变异,重点改进目标 '{target_obj}' (当前改进潜力: {improvement:.2f}): @@ -55,6 +59,8 @@ class MultiObjectiveMutationOperator: 2. 专门优化目标 '{target_obj}' 3. 可以引入新的数学概念或优化方法 4. 确保代码完整且可执行 + 5. 避免使用递归或限制递归深度 + 6. 函数名必须保持为: {analysis_info["function_name"]} 只返回最终的Python代码,不要包含任何解释。 """ diff --git a/src/lead/core/operators/mutation_analysis.py b/src/lead/core/operators/mutation_analysis.py index e0345b2..f297670 100644 --- a/src/lead/core/operators/mutation_analysis.py +++ b/src/lead/core/operators/mutation_analysis.py @@ -13,41 +13,59 @@ class MutationAnalysisOperator: { "target_objective": str, # 主要优化目标 "improvement_potential": Dict[str, float], # 各目标改进潜力 - "nearest_solution": Dict[str, float] # 参考前沿中最接近的解(可选) + "nearest_solution": Dict[str, float], # 参考前沿中最接近的解(可选) + "function_name": str # 函数名 } """ result = { "target_objective": None, "improvement_potential": {}, - "nearest_solution": None + "nearest_solution": None, + "function_name": self._extract_function_name(individual.code) } + # 处理空fitnesses的情况 + if not individual.fitnesses: + return result + # 计算各目标的改进潜力 for obj in objective_names: - if reference_front: - # 如果有参考前沿,计算与前沿中最好解的差距 - best_val = min(sol[obj] for sol in reference_front) - result["improvement_potential"][obj] = individual.fitnesses[obj] - best_val - else: - # 没有参考前沿,使用归一化值 - result["improvement_potential"][obj] = individual.fitnesses[obj] + if obj in individual.fitnesses: + if reference_front: + # 如果有参考前沿,计算与前沿中最好解的差距 + best_val = min(sol[obj] for sol in reference_front) + result["improvement_potential"][obj] = individual.fitnesses[obj] - best_val + else: + # 没有参考前沿,使用归一化值 + result["improvement_potential"][obj] = individual.fitnesses[obj] # 选择改进潜力最大的目标作为变异方向 - result["target_objective"] = max( - result["improvement_potential"].items(), - key=lambda x: x[1] - )[0] + if result["improvement_potential"]: + result["target_objective"] = max( + result["improvement_potential"].items(), + key=lambda x: x[1] + )[0] + else: + # 如果没有有效的改进潜力数据,选择第一个目标 + result["target_objective"] = objective_names[0] if objective_names else None # 如果有参考前沿,找到最接近的解 if reference_front: min_distance = float('inf') for sol in reference_front: distance = sum( - (individual.fitnesses[obj] - sol[obj]) ** 2 - for obj in objective_names + (individual.fitnesses[obj] - sol[obj]) ** 2 + for obj in objective_names if obj in individual.fitnesses ) if distance < min_distance: min_distance = distance result["nearest_solution"] = sol - return result \ No newline at end of file + return result + + def _extract_function_name(self, code: str) -> str: + """从代码中提取函数名""" + for line in code.split('\n'): + if line.strip().startswith('def '): + return line.split()[1].split('(')[0] + return "solution" # 默认函数名 \ No newline at end of file diff --git a/src/lead/problems/sort/evaluation.py b/src/lead/problems/sort/evaluation.py index 4521adc..8a00b8e 100644 --- a/src/lead/problems/sort/evaluation.py +++ b/src/lead/problems/sort/evaluation.py @@ -1,19 +1,27 @@ -from typing import Callable, List, Tuple +from typing import Callable, List, Tuple, Dict import time +import sys -def evaluate(sort_func: Callable[[List[int]], List[int]], dataset: List[Tuple[List[int], List[int]]]) -> float: - """评估排序算法的性能 +def evaluate(sort_func: Callable[[List[int]], List[int]], dataset: List[Tuple[List[int], List[int]]]) -> Dict[str, float]: + """多目标评估排序算法的性能 Args: sort_func: 排序函数 dataset: [(输入数组, 正确排序结果), ...] Returns: - float: 适应度分数,分数越小表示性能越好 + dict: 包含两个评估指标的字典,所有指标都是越小越好 + { + "time": 平均执行时间(秒), + "error": 错误率(0-1) + } """ total_time = 0 success_count = 0 + # 设置递归深度限制防止无限递归 + sys.setrecursionlimit(1000) + for input_arr, expected in dataset: try: # 复制输入数组,避免修改原数组 @@ -30,15 +38,24 @@ def evaluate(sort_func: Callable[[List[int]], List[int]], dataset: List[Tuple[Li total_time += execution_time success_count += 1 + except RecursionError: + print("递归深度超过限制,算法可能存在无限递归") + continue except Exception as e: print(f"测试用例执行失败: {str(e)}") continue if success_count == 0: - return float('inf') # 如果全部失败返回无穷大 + return { + "time": float('inf'), + "error": 1.0 + } - # 计算最终适应度:平均执行时间 * (1 + 错误率惩罚) + # 计算各项指标 avg_time = total_time / success_count error_rate = (len(dataset) - success_count) / len(dataset) - return avg_time * (1 + error_rate * 2) # 错误率会增加最终得分 \ No newline at end of file + return { + "time": avg_time, + "error": error_rate + } \ No newline at end of file diff --git a/src/lead/problems/sort/multi_objective_evolution.log b/src/lead/problems/sort/multi_objective_evolution.log new file mode 100644 index 0000000..a83cccc --- /dev/null +++ b/src/lead/problems/sort/multi_objective_evolution.log @@ -0,0 +1,2 @@ +Generation 0000 | Pareto Front Size: 2 | Timestamp: 2025-04-15T18:08:58.280070 +Generation 0001 | Pareto Front Size: 2 | Timestamp: 2025-04-15T18:10:29.667065 diff --git a/src/lead/problems/sort/multi_objective_history/20250415_180647/generation_0.json b/src/lead/problems/sort/multi_objective_history/20250415_180647/generation_0.json new file mode 100644 index 0000000..de00559 --- /dev/null +++ b/src/lead/problems/sort/multi_objective_history/20250415_180647/generation_0.json @@ -0,0 +1,67 @@ +{ + "generation": 0, + "population": [ + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n \n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi - 1)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while arr[i] < pivot:\n i += 1\n j -= 1\n while arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n def insertion_sort(arr):\n for i in range(1, len(arr)):\n key = arr[i]\n j = i - 1\n while j >= 0 and key < arr[j]:\n arr[j + 1] = arr[j]\n j -= 1\n arr[j + 1] = key\n\n if len(arr) <= 10:\n insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 0, + "fitnesses": { + "time": 0.07625889778137207, + "error": 0.75 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(sub_arr):\n if len(sub_arr) <= 10:\n return insertion_sort(sub_arr)\n else:\n pivot = sub_arr[len(sub_arr) // 2]\n left = [x for x in sub_arr if x < pivot]\n middle = [x for x in sub_arr if x == pivot]\n right = [x for x in sub_arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)\n\n return quicksort(arr)", + "generation": 1, + "fitnesses": { + "time": 0.08427751064300537, + "error": 0.0 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while arr[i] < pivot:\n i += 1\n j -= 1\n while arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n if len(arr) <= 10:\n return insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 1, + "fitnesses": { + "time": 0.1091681718826294, + "error": 0.0 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(sub_arr):\n if len(sub_arr) <= 10:\n return insertion_sort(sub_arr)\n else:\n pivot = sub_arr[len(sub_arr) // 2]\n left = [x for x in sub_arr if x < pivot]\n middle = [x for x in sub_arr if x == pivot]\n right = [x for x in sub_arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)\n\n return quicksort(arr)", + "generation": 0, + "fitnesses": { + "time": 0.17589008808135986, + "error": 0.0 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def counting_sort(arr):\n max_val = max(arr)\n min_val = min(arr)\n range_of_elements = max_val - min_val + 1\n count = [0] * range_of_elements\n output = [0] * len(arr)\n\n for num in arr:\n count[num - min_val] += 1\n\n for i in range(1, len(count)):\n count[i] += count[i - 1]\n\n for num in reversed(arr):\n output[count[num - min_val] - 1] = num\n count[num - min_val] -= 1\n\n return output\n\n def quick_sort(arr):\n if len(arr) < 2:\n return arr\n\n pivot = arr[len(arr) // 2]\n left = [x for x in arr if x < pivot]\n middle = [x for x in arr if x == pivot]\n right = [x for x in arr if x > pivot]\n\n return quick_sort(left) + middle + quick_sort(right)\n\n unique_elements = set(arr)\n \n if len(unique_elements) * 2 < len(arr):\n return counting_sort(arr)\n else:\n return quick_sort(arr)", + "generation": 0, + "fitnesses": { + "time": 0.2675272822380066, + "error": 0.0 + } + } + ], + "pareto_front": [ + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n \n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi - 1)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while arr[i] < pivot:\n i += 1\n j -= 1\n while arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n def insertion_sort(arr):\n for i in range(1, len(arr)):\n key = arr[i]\n j = i - 1\n while j >= 0 and key < arr[j]:\n arr[j + 1] = arr[j]\n j -= 1\n arr[j + 1] = key\n\n if len(arr) <= 10:\n insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 0, + "fitnesses": { + "time": 0.07625889778137207, + "error": 0.75 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(sub_arr):\n if len(sub_arr) <= 10:\n return insertion_sort(sub_arr)\n else:\n pivot = sub_arr[len(sub_arr) // 2]\n left = [x for x in sub_arr if x < pivot]\n middle = [x for x in sub_arr if x == pivot]\n right = [x for x in sub_arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)\n\n return quicksort(arr)", + "generation": 1, + "fitnesses": { + "time": 0.08427751064300537, + "error": 0.0 + } + } + ], + "objective_names": [ + "time", + "error" + ] +} \ No newline at end of file diff --git a/src/lead/problems/sort/multi_objective_history/20250415_180647/generation_1.json b/src/lead/problems/sort/multi_objective_history/20250415_180647/generation_1.json new file mode 100644 index 0000000..7d27005 --- /dev/null +++ b/src/lead/problems/sort/multi_objective_history/20250415_180647/generation_1.json @@ -0,0 +1,67 @@ +{ + "generation": 1, + "population": [ + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n \n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi - 1)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while arr[i] < pivot:\n i += 1\n j -= 1\n while arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n def insertion_sort(arr):\n for i in range(1, len(arr)):\n key = arr[i]\n j = i - 1\n while j >= 0 and key < arr[j]:\n arr[j + 1] = arr[j]\n j -= 1\n arr[j + 1] = key\n\n if len(arr) <= 10:\n insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 0, + "fitnesses": { + "time": 0.07625889778137207, + "error": 0.75 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(sub_arr):\n if len(sub_arr) <= 10:\n return insertion_sort(sub_arr)\n else:\n pivot = sub_arr[len(sub_arr) // 2]\n left = [x for x in sub_arr if x < pivot]\n middle = [x for x in sub_arr if x == pivot]\n right = [x for x in sub_arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)\n\n return quicksort(arr)", + "generation": 1, + "fitnesses": { + "time": 0.08427751064300537, + "error": 0.0 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(sub_arr):\n if len(sub_arr) <= 10:\n return insertion_sort(sub_arr)\n else:\n pivot = sub_arr[len(sub_arr) // 2]\n left = [x for x in sub_arr if x < pivot]\n middle = [x for x in sub_arr if x == pivot]\n right = [x for x in sub_arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)\n\n return quicksort(arr)", + "generation": 0, + "fitnesses": { + "time": 0.17589008808135986, + "error": 0.0 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while arr[i] < pivot:\n i += 1\n j -= 1\n while arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n if len(arr) <= 10:\n return insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 1, + "fitnesses": { + "time": 0.3470318913459778, + "error": 0.0 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n \n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi - 1)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while i < high and arr[i] < pivot:\n i += 1\n j -= 1\n while j > low and arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n def insertion_sort(arr):\n for i in range(1, len(arr)):\n key = arr[i]\n j = i - 1\n while j >= 0 and key < arr[j]:\n arr[j + 1] = arr[j]\n j -= 1\n arr[j + 1] = key\n\n if len(arr) <= 10:\n insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 1, + "fitnesses": { + "time": 0.24364137649536133, + "error": 0.75 + } + } + ], + "pareto_front": [ + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n \n def quicksort(arr, low, high):\n if low < high:\n pi = partition(arr, low, high)\n quicksort(arr, low, pi - 1)\n quicksort(arr, pi + 1, high)\n\n def partition(arr, low, high):\n pivot = arr[(low + high) // 2]\n i = low - 1\n j = high + 1\n\n while True:\n i += 1\n while arr[i] < pivot:\n i += 1\n j -= 1\n while arr[j] > pivot:\n j -= 1\n \n if i >= j:\n return j\n \n arr[i], arr[j] = arr[j], arr[i]\n\n def insertion_sort(arr):\n for i in range(1, len(arr)):\n key = arr[i]\n j = i - 1\n while j >= 0 and key < arr[j]:\n arr[j + 1] = arr[j]\n j -= 1\n arr[j + 1] = key\n\n if len(arr) <= 10:\n insertion_sort(arr)\n else:\n quicksort(arr, 0, len(arr) - 1)\n\n return arr", + "generation": 0, + "fitnesses": { + "time": 0.07625889778137207, + "error": 0.75 + } + }, + { + "code": "def advanced_sort(arr):\n if len(arr) < 2:\n return arr\n\n def insertion_sort(sub_arr):\n for i in range(1, len(sub_arr)):\n key = sub_arr[i]\n j = i - 1\n while j >= 0 and sub_arr[j] > key:\n sub_arr[j + 1] = sub_arr[j]\n j -= 1\n sub_arr[j + 1] = key\n return sub_arr\n\n def quicksort(sub_arr):\n if len(sub_arr) <= 10:\n return insertion_sort(sub_arr)\n else:\n pivot = sub_arr[len(sub_arr) // 2]\n left = [x for x in sub_arr if x < pivot]\n middle = [x for x in sub_arr if x == pivot]\n right = [x for x in sub_arr if x > pivot]\n return quicksort(left) + middle + quicksort(right)\n\n return quicksort(arr)", + "generation": 1, + "fitnesses": { + "time": 0.08427751064300537, + "error": 0.0 + } + } + ], + "objective_names": [ + "time", + "error" + ] +} \ No newline at end of file diff --git a/src/lead/problems/sort/problem_config.json b/src/lead/problems/sort/problem_config.json index 516dc9f..c654a77 100644 --- a/src/lead/problems/sort/problem_config.json +++ b/src/lead/problems/sort/problem_config.json @@ -1,23 +1,24 @@ { - "description": "实现一个高效的排序算法,要求时间复杂度尽可能低。算法需要处理各种情况:随机数组、接近有序的数组、逆序数组、包含大量重复元素的数组等。同时由于你拥有的评估时间仅有2秒,请确保你的算法在2秒内能够完成排序。而且你只能使用python语言。注意我们用于评估你的测试用例的数组大小为100000,请确保你的算法在100000个元素的数组上也能在2秒内完成排序。", + "description": "实现一个高效的排序算法,需要同时优化执行时间和正确性。算法需要处理各种情况:随机数组、接近有序的数组、逆序数组、包含大量重复元素的数组等。", "function_name": "advanced_sort", "input_format": "一个整数列表arr,包含需要排序的数字", "output_format": "返回排序后的列表,按照从小到大的顺序排列", "evaluation_timeout": 2, + "multi_objective": true, + "objective_names": ["time", "error"], "llm_config": { "api_key": "sk-5wHuJRuC2KDMINeYERru0zCCMrRTzmkrOMwpvBQr4EULV0Tv", "base_url": "https://xiaohumini.site/v1/chat/completions", - "model": "gemini-1.5-flash-latest", + "model": "gpt-4o-mini", "temperature": 0.5, "max_tokens": 4096 }, "evolution_params": { - "algorithm": "TU", + "algorithm": "MO", "population_size": 5, "generations": 2, "mutation_rate": 0.3, - "crossover_rate": 0.5, - "F": 0.2, - "CR": 0.5 + "crossover_rate": 0.7, + "tournament_size": 3 } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/lead/scripts/run_evolution.py b/src/lead/scripts/run_evolution.py index 7187e46..b1967b7 100644 --- a/src/lead/scripts/run_evolution.py +++ b/src/lead/scripts/run_evolution.py @@ -1,7 +1,9 @@ import os import sys import argparse +import json from lead.core.evolution import EvolutionEngine +from lead.core.multi_objective.evolution import MultiObjectiveEvolutionEngine from lead.config.settings import PROBLEMS_DIR def parse_args(): @@ -14,6 +16,19 @@ def parse_args(): help="种群大小") return parser.parse_args() +def load_engine_config(problem_path): + """加载问题配置并决定使用哪种引擎""" + config_path = os.path.join(problem_path, "problem_config.json") + with open(config_path, "r", encoding="utf-8") as f: + config = json.load(f) + + if config.get("multi_objective", False): + print("使用多目标进化引擎") + return MultiObjectiveEvolutionEngine(problem_path) + else: + print("使用单目标进化引擎") + return EvolutionEngine(problem_path) + def main(): args = parse_args() problem_path = os.path.join(PROBLEMS_DIR, args.problem) @@ -27,7 +42,7 @@ def main(): print(f"开始进化求解问题: {args.problem}") print(f"配置: 代数={args.generations or '默认'},种群大小={args.population or '默认'}") - engine = EvolutionEngine(problem_path) + engine = load_engine_config(problem_path) engine.run_evolution( generations=args.generations, population_size=args.population @@ -36,4 +51,4 @@ def main(): print("进化完成!") if __name__ == "__main__": - main() \ No newline at end of file + main() \ No newline at end of file