遗传算法库jmetal最简示例

这个示例展示了如何使用 JMetal 进行简单的多目标优化问题。你可以根据需要修改问题、算法参数和算子来适应不同的优化问题。

遗传算法的简单处理流程

摘自 https://pymoo.org/algorithms/soo/ga.html
https://github.com/Mycenae/PaperWeekly/blob/master/jMetalPy.md

NSGAII 算法特点:

  • 种群大小保持一致:在 NSGA-II 中,父代种群和子代种群的大小是相同的。如果初始种群包含𝑁 个个体,那么在每一代中,子代的数量也将是 𝑁。
  • 选择机制:在合并的种群中(父代和子代的组合),算法根据非支配等级和拥挤度距离选择出 𝑁 个个体作为下一代的种群。因此,最终的可行解个数不会超过初始种群的个体数。
  • 多目标优化:尽管最终的可行解个数最多为 𝑁,但 NSGA-II 的目标是找到多个 Pareto 最优解,最终返回的解可能在目标空间中形成一个 Pareto 前沿。

NSGAIII 算法特点:

  • NSGAII 因为使用非支配排序和拥挤度距离来选择个体, 当目标函数数量较多时候, 计算效率会变差.
  • NSGAIII 的优势主要体现在目标函数较多的情况下, 它使用均匀分布的参考点来引导个体的选择, 能更加有效覆盖Pareto前沿, 解的多样性会更好, 速度更快, 尤其是在高维目标(即多目标函数)

相关类库

  • jmetal-algorithm :包含各种遗传算法和算法执行Main函数代码,
  • jmetal-core : 包含各种基础类
  • jmetal-problem : 定义了 AbstractDoubleProblem 和 AbstractIntegerProblem 问题域抽象, 也定义了很多常用的问题模型, 比如 ZDT1 性能测试用问题, 比如 Traveling Salesman Problem 等.
  • jmetal-lab: 可选包, 包含作者的一些学习和测试代码, 我们可以借鉴一下.

编程提示:

  • 问题定义示例可以参考 ZDT1
  • 求解器主函数可以参考 NSGAIIRunner
  • NSGAII 算法是按照最小值做优化的, 所以如果要求最大值, objective 函数要乘负一.
  • 在新版6.6中, 貌似没找到算法迭代 Observer, 这样对于观察每代的情况不是很方便, Problem 的 evaluate() 会对个体评价, 可以很容易访问到的决策变量变量(即基因)和objective(即适应度), 所以可以在这个方法中加上日志来观察演进变化.

关于约束问题

  • 需要说明的是, 所有的遗传算法都是随机搜索算法,可能会生成不满足约束的解, 对于多目标函数尤为如此.
  • NSGAII/SPEA2 , 尤其是在解决无约束多目标优化问题的性能而被广泛使用, 对于约束, 这些算法底层使用了惩罚函数来柔性评估约束, 这种方法并不能保证生成的解一定满足约束, 只是使得满足约束的解有更大可能被选择.
  • 推荐使用BinaryTournamentSelection 选择算子, 它实现"feasibility rule"的一种选择操作。BinaryTournamentSelection在选择操作中,会随机选择两个解进行比较,如果两个解在Pareto支配关系中可以比较,那么就选择支配对方的解;如果两个解在Pareto支配关系中无法比较,那么就选择满足约束的解;如果两个解都满足约束,或者都不满足约束,那么就随机选择一个解。 但是,需要注意的是,即使使用了"feasibility rule",也不能保证所有的解都满足约束,因为遗传算法是一种随机搜索算法,可能会生成不满足约束的解.
  • 在代码中可通过设置 solution.constraints()[i] 取值来设定约束条件, 即给出惩罚值, 取值>=0为满足约束, 取值小于0为违反约束. 针对违法约束, 不要设置成固定的惩罚值, 应该按照远离约束条件的程度来设定惩罚值, 这样后代才有可能进化; 满足约束, 可以直接设定一个定值.
  • 为了保证结果一定满足约束, 方法有:
    . 可以在算法结果solutionSet中过滤掉哪些不符合约束的solution
    . 在将约束条件转换成目标函数.
    . 对于违反约束的情况, 可在Problem 的 evaluate() 方法中, 主动设置 objective 为一个较差的结果.

代码解释:

  • 定义问题:可以直接使用内置的 ZDT1 问题,它是一个经典的双目标优化问题, 我也自定义了两个简单的问题。
  • 定义算法:使用 NSGA-II 算法,并设置种群大小和迭代次数。
  • 设置交叉和变异算子:这里使用 SBX 交叉和多项式变异。
  • 设置选择算子:使用二进制锦标赛选择。
  • 运行算法:执行算法并记录执行时间。
  • 打印结果:打印每个解的详细信息。
  • 输出结果到文件:将结果输出到文件中。
package aaaaaaa;

import java.util.ArrayList;
import java.util.List;

import org.uma.jmetal.algorithm.Algorithm;
import org.uma.jmetal.algorithm.examples.AlgorithmRunner;
import org.uma.jmetal.algorithm.multiobjective.nsgaii.NSGAIIBuilder;
import org.uma.jmetal.operator.crossover.CrossoverOperator;
import org.uma.jmetal.operator.crossover.impl.SBXCrossover;
import org.uma.jmetal.operator.mutation.MutationOperator;
import org.uma.jmetal.operator.mutation.impl.PolynomialMutation;
import org.uma.jmetal.operator.selection.SelectionOperator;
import org.uma.jmetal.operator.selection.impl.BinaryTournamentSelection;
import org.uma.jmetal.problem.Problem;
import org.uma.jmetal.problem.doubleproblem.impl.AbstractDoubleProblem;
import org.uma.jmetal.problem.multiobjective.zdt.ZDT1;
import org.uma.jmetal.solution.doublesolution.DoubleSolution;
import org.uma.jmetal.util.AbstractAlgorithmRunner;
import org.uma.jmetal.util.ConstraintHandling;
import org.uma.jmetal.util.JMetalLogger;
import org.uma.jmetal.util.comparator.RankingAndCrowdingDistanceComparator;
import org.uma.jmetal.util.fileoutput.SolutionListOutput;
import org.uma.jmetal.util.fileoutput.impl.DefaultFileOutputContext;

public class App {

	public static void main(String[] args) {
		// 1. 定义问题
		Problem<DoubleSolution> problem = null;
		problem = new ZDT1();
		problem = new TwoObjectiveProblem();
		problem = new PortfolioProblem();
		problem = new SquareDistanceProblem();

		// 2. 设置交叉和变异算子 和 设置选择算子

		// 定义交叉操作: SBX交叉
		double crossoverProbability = 0.9;
		double crossoverDistributionIndex = 20.0;
		CrossoverOperator<DoubleSolution> crossover = new SBXCrossover(crossoverProbability,
				crossoverDistributionIndex);

		// 定义变异操作: 多项式变异
		double mutationProbability = 1.0 / problem.numberOfVariables();
		double mutationDistributionIndex = 20.0;
		MutationOperator<DoubleSolution> mutation = new PolynomialMutation(mutationProbability,
				mutationDistributionIndex);

		// 定义选择操作: 二元竞标赛选择
		SelectionOperator<List<DoubleSolution>, DoubleSolution> selection = new BinaryTournamentSelection<>(
				new RankingAndCrowdingDistanceComparator<>());

		// 3. 迭代次数和种群大小
		int populationSize = 100;

		// 4. 定义算法(NSGA-II)
//		Algorithm<List<DoubleSolution>> algorithm = new NSGAIIBuilder<>(problem, crossover, mutation, populationSize)
//				.setSelectionOperator(selection).setMaxEvaluations(25000).build();
		Algorithm<List<DoubleSolution>> algorithm = new NSGAIIBuilder<>(problem, crossover, mutation, populationSize)
				.setSelectionOperator(selection).setMaxEvaluations(25000).build();

		// 5. 运行算法
		AlgorithmRunner algorithmRunner = new AlgorithmRunner.Executor(algorithm).execute();
		List<DoubleSolution> solutionSet = algorithm.result();

		long computingTime = algorithmRunner.getComputingTime();
		JMetalLogger.logger.info("Total execution time: " + computingTime + "ms");

		// 6. 打印非支配排序结果,每个solution包含决策变量取值和目标函数取值.
		for (DoubleSolution solution : solutionSet) {
			JMetalLogger.logger.info("Solution: " + solution);
		}
		JMetalLogger.logger.info("Solution Count: " + solutionSet.size());

		// 7. save to tsv files
		new SolutionListOutput(solutionSet).setVarFileOutputContext(new DefaultFileOutputContext("VAR.csv", ","))
				.setFunFileOutputContext(new DefaultFileOutputContext("FUN.csv", ",")).print();
		AbstractAlgorithmRunner.printFinalSolutionSet(solutionSet);
	}

}

/*
 * 演示单目标函数, 多决策变量, 另外带有约束条件处理过程
 * 
 */
class SquareDistanceProblem extends AbstractDoubleProblem {
	private int generationCount;

	public SquareDistanceProblem() {
		name("SquareDistance Problem");
		numberOfObjectives(1); // 设置目标函数数量
		numberOfConstraints(2);// 设置约束条件数量

		double maxWidth = 998;
		double maxHeight = 680;
		Integer numberOfVariables = 4;

		List<Double> lowerLimit = new ArrayList<>(numberOfVariables);
		List<Double> upperLimit = new ArrayList<>(numberOfVariables);

		// x1
		lowerLimit.add(0.0);
		upperLimit.add(maxWidth);

		// y1
		lowerLimit.add(0.0);
		upperLimit.add(maxHeight);

		// x2
		lowerLimit.add(0.0);
		upperLimit.add(maxWidth);

		// y2
		lowerLimit.add(0.0);
		upperLimit.add(maxHeight);

		variableBounds(lowerLimit, upperLimit);
	}

	/** Evaluate() method */
	public DoubleSolution evaluate(DoubleSolution solution) {
		generationCount = generationCount + 1;
		System.out.println("Generation: " + generationCount);
		double x1 = solution.variables().get(0);
		double y1 = solution.variables().get(1);
		double x2 = solution.variables().get(2);
		double y2 = solution.variables().get(3);
		double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
		double objective = -1 * distance;
		solution.objectives()[0] = objective;
		evaluateConstraints(solution);
		System.out.println("Feasible: " + ConstraintHandling.isFeasible(solution));

		// 当约束不满足时候, 调整objective为较差的值, 这样遗传算法为抛弃这一后代
		if (ConstraintHandling.isFeasible(solution) == false) {
			solution.objectives()[0] = 1000;
		}

		return solution;
	}

	/** EvaluateConstraints() method */
	public void evaluateConstraints(DoubleSolution solution) {

		JMetalLogger.logger.info("evaluateConstraints");
		double x1 = solution.variables().get(0);
		double y1 = solution.variables().get(1);
		double x2 = solution.variables().get(2);
		double y2 = solution.variables().get(3);

		// https://jmetal.readthedocs.io/en/latest/constraints.html
		double contraintX = x1 - x2; // 约束条件为x1-x2>=0, 如果<0被认为违反约束
		solution.constraints()[0] = contraintX;// ;contraintX
		double contraintY = y1 - y2; // 约束条件为y1-y2>=0, 如果<0被认为违反约束
		solution.constraints()[1] = contraintY;// contraintY;
	}
}

/*
 * 定义两个目标函数的示例 单决策变量, 该优化会有很多个解
 */
class TwoObjectiveProblem extends AbstractDoubleProblem {
	private int generationCount;

	public TwoObjectiveProblem() {
		name("TwoObjectiveProblem");
		numberOfObjectives(2);// 设置目标函数数量
		numberOfConstraints(2);// 设置约束条件数量

		// Set variable bounds
		List<Double> lowerLimit = new ArrayList<>();
		lowerLimit.add(-5.0);
		List<Double> upperLimit = new ArrayList<>();
		upperLimit.add(5.0);
		variableBounds(lowerLimit, upperLimit);
	}

	public DoubleSolution evaluate(DoubleSolution solution) {
		generationCount = generationCount + 1;
		System.out.println("Generation: " + generationCount);
		double x = solution.variables().get(0);
		solution.objectives()[0] = x * x; // f1 = x^2
		solution.objectives()[1] = (x - 2) * (x - 2); // f2 = (x - 2)^2
		evaluateConstraints(solution);
		System.out.println("Feasible: " + ConstraintHandling.isFeasible(solution));
		return solution;
	}

	/** EvaluateConstraints() method */
	public void evaluateConstraints(DoubleSolution solution) {
		JMetalLogger.logger.info("evaluateConstraints");
		double x = solution.variables().get(0);

		// https://jmetal.readthedocs.io/en/latest/constraints.html
		// 设置约束条件为 1.5>x>1
		double contraintX = -1;
		if (x > 1 && x < 1.5) {
			contraintX = 1; // OK
		} else if (x <= 1) {
			contraintX = -1 * (1 - x); // 惩罚
		} else {
			contraintX = -1 * (x - 1.5); // 惩罚
		}
		solution.constraints()[0] = contraintX;
	}
}

/*
 * 假设了三个资产的预期收益率分别为5%,7%,9%, 风险分别为1%,2%,3%。 在实际应用中,这些值通常需要从历史数据中计算得出。
 */
class PortfolioProblem extends AbstractDoubleProblem {
	public PortfolioProblem() {
		numberOfObjectives(2); // Minimize risk and maximize return
		numberOfConstraints(1);

		// Set variable bounds
		// 3 stock share, 决策变量为它们在投资组合中的占比
		List<Double> lowerLimit = new ArrayList<>();
		lowerLimit.add(0.0);
		lowerLimit.add(0.0);
		lowerLimit.add(0.0);
		List<Double> upperLimit = new ArrayList<>();
		upperLimit.add(1.0);
		upperLimit.add(1.0);
		upperLimit.add(1.0);
		variableBounds(lowerLimit, upperLimit);
	}

	public DoubleSolution evaluate(DoubleSolution solution) {
		double[] x = new double[solution.variables().size()];
		double sum = 0.0;
		for (int i = 0; i < solution.variables().size(); i++) {
			x[i] = solution.variables().get(i);
			sum += x[i];
		}

		// 约束处理: 将投资比例标准化, Add the constraint that the sum of the weights must be 1
		// 这里没有使用 solution.constraints() 来进行约束限定, 因为solution.constraints()不是强制约束
		// 所以在变量层面做了归一化处理.
		if (sum > 0) {
			for (int i = 0; i < solution.variables().size(); i++) {
				x[i] /= sum; // 标准化为比例
			}
		}
		for (int i = 0; i < solution.variables().size(); i++) {
			solution.variables().set(i, x[i]);
		}

		// Calculate the portfolio return and risk
		// Example returns, 收益率分别为5%,7%,9%
		double return_ = x[0] * 0.05 + x[1] * 0.07 + x[2] * 0.09;
		// Example risks, 风险分别为1%,2%,3%
		double risk_ = x[0] * 0.01 + x[1] * 0.02 + x[2] * 0.03;

		// Set the objectives
		solution.objectives()[0] = -1 * return_; // We want to maximize the return, so we minimize -return
		solution.objectives()[1] = risk_; // We want to minimize the risk

		if (sum != 1) {
			solution.constraints()[0] = -100 * sum; // 惩罚
		} else {
			solution.constraints()[0] = 1; // OK
		}
		System.out.println("Feasible: " + ConstraintHandling.isFeasible(solution));

		return solution;
	}

}


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>sss</groupId>
  <artifactId>aaaaaaa</artifactId>
  <version>0.0.1-SNAPSHOT</version>
   <dependencies> 
        <dependency>
            <groupId>org.uma.jmetal</groupId>
            <artifactId>jmetal-core</artifactId>
            <version>6.6</version>
        </dependency>   
        <dependency>
            <groupId>org.uma.jmetal</groupId>
            <artifactId>jmetal-algorithm</artifactId>
            <version>6.6</version>
        </dependency>
        <dependency>
            <groupId>org.uma.jmetal</groupId>
            <artifactId>jmetal-problem</artifactId>
            <version>6.6</version>
        </dependency> 
        <dependency>
            <groupId>org.uma.jmetal</groupId>
            <artifactId>jmetal-lab</artifactId>
            <version>6.6</version>
        </dependency> 
</dependencies>
</project>
posted @   harrychinese  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示