Commit 41cab0d7 authored by Tong Li's avatar Tong Li

优化

parent 7c25b0fe
...@@ -10,6 +10,32 @@ public class FileHelper { ...@@ -10,6 +10,32 @@ public class FileHelper {
private static final String LOG_FILE = "schedule_log.txt"; private static final String LOG_FILE = "schedule_log.txt";
private static final String LOG_FILE_PATH = "log/"; private static final String LOG_FILE_PATH = "log/";
// 日志级别
private static final int LOG_LEVEL_DEBUG = 0;
private static final int LOG_LEVEL_INFO = 1;
private static final int LOG_LEVEL_WARN = 2;
private static int currentLogLevel = LOG_LEVEL_INFO;
// 局部搜索优化
public static void log(String message) {
log(message, LOG_LEVEL_INFO, true);
}
public static void log(String message, boolean enableLogging) {
log(message, LOG_LEVEL_INFO, enableLogging);
}
public static void log(String message, int level) {
log(message, level, false);
}
public static void log(String message, int level, boolean enableLogging) {
if (enableLogging && level >= currentLogLevel) {
writeLogFile(message);
}
}
public static void writeLogFile(String message) { public static void writeLogFile(String message) {
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))+"-"; String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))+"-";
......
...@@ -133,7 +133,7 @@ public class Chromosome { ...@@ -133,7 +133,7 @@ public class Chromosome {
private TreeMap<String, Material> materials = new TreeMap<>(); private TreeMap<String, Material> materials = new TreeMap<>();
// private List<Material> materials = new ArrayList<>(); // private List<Material> materials = new ArrayList<>();
private List<String> materialIds = new ArrayList<>(); private List<String> materialIds = new ArrayList<>();
/* /*
...@@ -142,6 +142,12 @@ public class Chromosome { ...@@ -142,6 +142,12 @@ public class Chromosome {
private double[] Objectives = new double[0]; // 多目标值:[Makespan, TotalFlowTime, TotalChangeover, LoadStd, Delay] private double[] Objectives = new double[0]; // 多目标值:[Makespan, TotalFlowTime, TotalChangeover, LoadStd, Delay]
private double[] MaxObjectives = new double[0]; // private double[] MaxObjectives = new double[0]; //
private double[] MinObjectives = new double[0]; // private double[] MinObjectives = new double[0]; //
private double[] weights = new double[0];
/**
* 各目标维度的理论下界(用于计算 Gap = (current - lowerBound) / lowerBound)。
* 由 KpiLowerBoundCalculator 在 decode 后计算并填入。
*/
private double[] LowerBoundObjectives = new double[0];
private int Rank; // 非支配排序等级(1最优) private int Rank; // 非支配排序等级(1最优)
private double CrowdingDistance =0; // 拥挤距离 越小越优 private double CrowdingDistance =0; // 拥挤距离 越小越优
/* /*
...@@ -255,7 +261,7 @@ public class Chromosome { ...@@ -255,7 +261,7 @@ public class Chromosome {
* *
* @return 不在 allOperations 中的 GAScheduleResult 列表;若均存在则返回空列表 * @return 不在 allOperations 中的 GAScheduleResult 列表;若均存在则返回空列表
*/ */
public List<GAScheduleResult> getResultsNotInAllOperations() { public List<GAScheduleResult> checkResultsNotInAllOperations1() {
if (Result == null || Result.isEmpty()) { if (Result == null || Result.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
...@@ -265,7 +271,7 @@ public class Chromosome { ...@@ -265,7 +271,7 @@ public class Chromosome {
Set<Integer> allOpIds = allOperations.stream() Set<Integer> allOpIds = allOperations.stream()
.map(com.aps.entity.basic.Entry::getId) .map(com.aps.entity.basic.Entry::getId)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
List<GAScheduleResult> NotInAllOperations= Result.stream() List<GAScheduleResult> NotInAllOperations= Result.stream()
.filter(r -> !allOpIds.contains(r.getOperationId())) .filter(r -> !allOpIds.contains(r.getOperationId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
...@@ -281,8 +287,52 @@ public class Chromosome { ...@@ -281,8 +287,52 @@ public class Chromosome {
* *
* @return 存在则返回 true,否则返回 false * @return 存在则返回 true,否则返回 false
*/ */
public boolean hasResultNotInAllOperations() { // public boolean hasResultNotInAllOperations1() {
return !getResultsNotInAllOperations().isEmpty(); // / return !getResultsNotInAllOperations1().isEmpty();
// }
/**
* LNS 需要:创建染色体的深拷贝(machineSelection 和 operationSequencing 独立副本)
* 大对象(globalOpList / allOperations / orders / materials)共享引用
*/
public Chromosome deepCopy() {
Chromosome copy = new Chromosome();
if (machineSelection != null) {
copy.machineSelection = new CopyOnWriteArrayList<>(machineSelection);
copy.machineStrDirty = true;
}
if (operationSequencing != null) {
copy.operationSequencing = new CopyOnWriteArrayList<>(operationSequencing);
copy.operationStrDirty = true;
}
copy.geneStrDirty = true;
if (Objectives != null) copy.Objectives = Arrays.copyOf(Objectives, Objectives.length);
if (WeightedObjectives != null) copy.WeightedObjectives = Arrays.copyOf(WeightedObjectives, WeightedObjectives.length);
if (fitnessLevel != null) copy.fitnessLevel = Arrays.copyOf(fitnessLevel, fitnessLevel.length);
if (LowerBoundObjectives != null) copy.LowerBoundObjectives = Arrays.copyOf(LowerBoundObjectives, LowerBoundObjectives.length);
copy.WeightedObjective = WeightedObjective;
copy.Makespan = Makespan;
copy.TotalFlowTime = TotalFlowTime;
copy.TotalChangeoverTime = TotalChangeoverTime;
copy.MachineLoadStd = MachineLoadStd;
copy.Fitness = Fitness;
copy.Rank = Rank;
copy.CrowdingDistance = CrowdingDistance;
copy.gsOrls = gsOrls;
copy.generateType = generateType + "_copy";
copy.globalParamSnapshot = globalParamSnapshot;
copy.objectiveWeights = objectiveWeights;
copy.globalOpList = globalOpList;
copy.allOperations = allOperations;
copy.orders = orders;
copy.InitMachines = InitMachines;
copy.materials = materials;
copy.materialIds = materialIds;
copy.OperatRel = OperatRel;
copy.orderMaterials = orderMaterials;
copy.Machines = Machines;
if (Result != null) copy.Result = new CopyOnWriteArrayList<>(Result);
return copy;
} }
} }
...@@ -85,6 +85,9 @@ public class Entry { ...@@ -85,6 +85,9 @@ public class Entry {
* 工序顺序 * 工序顺序
*/ */
private int sequence; private int sequence;
private double minProcessingTime; // 加工时间 (秒)
/** /**
* 可选设备列表 * 可选设备列表
*/ */
......
package com.aps.service.Algorithm;
import com.aps.common.util.FileHelper;
import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.*;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.basic.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
/**
* 自适应大邻域搜索(ALNS)算法。
*
* <p>核心思想:
* <ol>
* <li>多个 Destroy 算子(破坏解的一部分) + 多个 Repair 算子(修复被破坏的解)</li>
* <li>自适应权重:根据算子表现动态调整选择概率</li>
* <li>模拟退火接受准则:以一定概率接受劣解,概率随迭代递减</li>
* </ol>
*
* <p>与 VNS 的区别:
* <ul>
* <li>VNS 系统性地切换邻域结构(换设备 → 工序前移 → 工序交换)</li>
* <li>ALNS 随机选择 destroy/repair 组合,按权重进行 roulette wheel 选择</li>
* </ul>
*
* <p>架构:
* <pre>
* ALNS.search(chromosome, tabuSearch, vns, decoder, machines)
* │
* ├─ 每个迭代:
* │ ├─ roulette select destroy operator
* │ ├─ roulette select repair operator
* │ ├─ destroy(chromosome) → partial solution
* │ ├─ repair(partial solution) → candidate
* │ ├─ tabu check
* │ ├─ localSearch(candidate)
* │ ├─ SA acceptance
* │ └─ update operator weights
* │
* └─ return best
* </pre>
*
* 作者:佟礼
*/
public class AdaptiveLargeNeighborhoodSearch {
// ==================== 随机数 ====================
private final Random rnd = new Random();
// ==================== ALNS 核心参数 ====================
private static final int MAX_ITERATIONS = 120; // 最大迭代次数
private static final int MAX_NO_IMPROVE_ITERATIONS = 25; // 最大连续无改进迭代
private static final int SEGMENT_SIZE = 10; // 权重更新段大小(每 SEGMENT_SIZE 迭代更新一次权重)
private static final double DESTROY_RATIO_MIN = 0.10; // 最小破坏比例(提高以增强探索)
private static final double DESTROY_RATIO_MAX = 0.40; // 最大破坏比例
private static final int MAX_RETRY_ATTEMPTS = 2; // 最多重试次数(首次失败后换组合)
// ==================== 自适应权重参数 ====================
private static final double INITIAL_WEIGHT = 1.0; // 初始权重
private static final double REACTION_FACTOR = 0.7; // 反应因子(历史权重与新分的比例)
private static final double MIN_WEIGHT = 0.1; // 最小权重(防止算子完全熄灭)
// ==================== 分数增量(越大越好) ====================
private static final double SCORE_NEW_BEST = 3.0; // 发现新的全局最优解
private static final double SCORE_BETTER = 1.5; // 比当前解更好
private static final double SCORE_ACCEPTED = 0.8; // 劣解被接受
private static final double SCORE_REJECTED = 0.1; // 劣解被拒绝
// ==================== 模拟退火参数 ====================
private static final double INITIAL_TEMPERATURE = 0.5; // 初始温度
private static final double COOLING_RATE = 0.98; // 冷却速率(加快冷却,使后期更聚焦)
private static final double FINAL_TEMPERATURE = 0.01; // 最终温度
// ==================== 改进判断参数 ====================
private static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 1e-11;
private static final double MINOR_IMPROVEMENT_THRESHOLD = 0.0;
// ==================== 多轮修复参数 ====================
private static final int MULTI_PASS_COUNT = 3; // 多轮修复尝试次数
// ==================== 时间预算 ====================
private static final long ALNS_TIME_BUDGET_MS = 15L * 60L * 1000L;
private static final long ALNS_PER_ITER_BUDGET_MS = 17L * 1000L;
// ==================== 依赖项 ====================
private final List<Entry> allOperations;
private final FitnessCalculator fitnessCalculator;
private final List<Order> orders;
private final TreeMap<String, Material> materials;
private final List<GroupResult> entryRel;
private final Map<Integer, Entry> entryByIds;
// ==================== 缓存解码数据 ====================
private List<Machine> cachedMachines;
private List<Order> cachedOrders;
private List<GroupResult> cachedEntryRel;
private TreeMap<String, Material> cachedMaterials;
private List<Entry> cachedAllOperations;
// ==================== 算子注册表 ====================
private final List<DestroyOperator> destroyOperators = new ArrayList<>();
private final List<RepairOperator> repairOperators = new ArrayList<>();
private double[] destroyWeights;
private double[] destroyScores;
private int[] destroyUseCount;
private double[] repairWeights;
private double[] repairScores;
private int[] repairUseCount;
// ==================== 构造函数 ====================
public AdaptiveLargeNeighborhoodSearch(List<Entry> allOperations, List<Order> orders,
TreeMap<String, Material> materials,
List<GroupResult> entryRel,
FitnessCalculator fitnessCalculator) {
this.allOperations = allOperations;
this.fitnessCalculator = fitnessCalculator;
this.orders = orders;
this.materials = materials;
this.entryRel = entryRel;
Map<Integer, Object> mp = buildEntryKey();
this.entryByIds = (Map<Integer, Entry>) mp.get(1);
// 预缓存解码数据
this.cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(allOperations), Entry.class);
this.cachedOrders = ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(orders), Order.class);
this.cachedEntryRel = ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(entryRel), GroupResult.class);
this.cachedMaterials = ProductionDeepCopyUtil.deepCopyTreeMap(
materials, String.class, Material.class);
// 注册 destroy 算子
registerDestroyOperators();
// 注册 repair 算子
registerRepairOperators();
// 初始化权重
initWeights();
}
// ==================== 算子注册 ====================
private void registerDestroyOperators() {
// 1. 随机换设备:随机选择部分工序更换机器
destroyOperators.add(new DestroyOperator("RandomMachineChange", this::destroyRandomMachineChange));
// 2. 瓶颈工序移除:移除瓶颈设备上的工序
destroyOperators.add(new DestroyOperator("BottleneckOpRemoval", this::destroyBottleneckOps));
// 3. 延迟工序打乱:打乱导致延迟的工序
destroyOperators.add(new DestroyOperator("DelayOpShuffle", this::destroyDelayOps));
// 4. 随机区间打乱:随机打乱一段连续的工序序列
destroyOperators.add(new DestroyOperator("RandomSegmentShuffle", this::destroyRandomSegment));
// 5. 负载均衡迁移:将超载机器上的工序迁移到低负载机器
destroyOperators.add(new DestroyOperator("LoadBalanceTransfer", this::destroyLoadBalanceTransfer));
}
private void registerRepairOperators() {
// 1. 多轮修复:多次调用 VNS 邻域生成,选最优结果
repairOperators.add(new RepairOperator("MultiPassRepair", this::repairMultiPass));
// 2. 贪婪换设备:对打乱的工序重新选择最优机器
repairOperators.add(new RepairOperator("GreedyMachineReassign", this::repairGreedyMachine));
// 3. 局部搜索修复:解码后通过 VNS 生成邻域
repairOperators.add(new RepairOperator("LocalSearchRepair", this::repairLocalSearch));
}
private void initWeights() {
destroyWeights = new double[destroyOperators.size()];
destroyScores = new double[destroyOperators.size()];
destroyUseCount = new int[destroyOperators.size()];
Arrays.fill(destroyWeights, INITIAL_WEIGHT);
repairWeights = new double[repairOperators.size()];
repairScores = new double[repairOperators.size()];
repairUseCount = new int[repairOperators.size()];
Arrays.fill(repairWeights, INITIAL_WEIGHT);
}
// ====================================================================
// 搜索主循环
// ====================================================================
/**
* ALNS 搜索主循环。
*
* @param chromosome 初始解
* @param tabuSearch 禁忌表(多算法共享)
* @param vns 提供邻域生成与局部搜索
* @param decoder 解码器
* @param machines 机器列表
* @return 优化后的最优解
*/
public Chromosome search(Chromosome chromosome, TabuSearch tabuSearch,
VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines) {
FileHelper.writeLogFile("ALNS - 开始执行");
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
double currentBestFitness = best.getFitness();
int iterations = 0;
int improveCount = 0;
int noImprovementCount = 0;
double temperature = INITIAL_TEMPERATURE;
// 时间预算
long startTimeMs = System.currentTimeMillis();
long remainingBudgetMs = Math.max(5L * 60L * 1000L, ALNS_TIME_BUDGET_MS / 2);
int timeBasedMaxIter = (int) Math.max(20, remainingBudgetMs / ALNS_PER_ITER_BUDGET_MS);
int maxIterations = Math.min(MAX_ITERATIONS, Math.max(40, timeBasedMaxIter));
FileHelper.writeLogFile(String.format(
"ALNS - 参数: 最大迭代=%d, 初始温度=%.3f, 冷却率=%.3f, 破坏比例=%.0f%%-%.0f%%, 时间预算=%.1fmin",
maxIterations, INITIAL_TEMPERATURE, COOLING_RATE,
DESTROY_RATIO_MIN * 100, DESTROY_RATIO_MAX * 100,
(double) remainingBudgetMs / 60000.0));
for (int iter = 0; iter < maxIterations; iter++) {
iterations++;
decoder.DelOrder(current);
// ---- 1. 自适应选择 destroy + repair 算子 ----
int destroyIdx = rouletteSelect(destroyWeights);
int repairIdx = rouletteSelect(repairWeights);
DestroyOperator destroyOp = destroyOperators.get(destroyIdx);
RepairOperator repairOp = repairOperators.get(repairIdx);
Chromosome repaired = null;
int repairAttempt = 0;
// ---- 重试机制:最多尝试 MAX_RETRY_ATTEMPTS 次不同的 destroy+repair 组合 ----
for (; repairAttempt < MAX_RETRY_ATTEMPTS; repairAttempt++) {
// ---- 2. Destroy:破坏当前解 ----
Chromosome destroyed = destroyOp.apply(current, DESTROY_RATIO_MIN, DESTROY_RATIO_MAX);
if (destroyed == null) {
// 换其他 destroy 算子重试
int newDestroyIdx = (destroyIdx + 1 + rnd.nextInt(destroyOperators.size() - 1)) % destroyOperators.size();
destroyIdx = newDestroyIdx;
destroyOp = destroyOperators.get(destroyIdx);
continue;
}
// ---- 3. Repair:修复被破坏的解 ----
repaired = repairOp.apply(destroyed, vns, decoder, machines);
if (repaired != null) {
break; // 修复成功,退出重试
}
// 修复失败,换算子重试
int newRepairIdx = (repairIdx + 1 + rnd.nextInt(repairOperators.size() - 1)) % repairOperators.size();
repairIdx = newRepairIdx;
repairOp = repairOperators.get(repairIdx);
}
if (repaired == null) {
noImprovementCount++;
updateOperatorScores(destroyIdx, repairIdx, SCORE_REJECTED, false);
continue;
}
// ---- 4. 禁忌检查 ----
boolean tabuHit = tabuSearch.isChromosomeTabu(repaired);
// ---- 5. 解码 ----
decode(decoder, repaired, machines);
tabuSearch.addChromosomeToTabu(repaired);
// ---- 6. 接受准则 ----
boolean betterThanBest = isBetter(repaired, best);
boolean betterThanCurrent = isBetter(repaired, current);
boolean accept;
double score;
if (betterThanBest) {
// 渴望准则:无条件接受
accept = true;
score = SCORE_NEW_BEST;
} else if (betterThanCurrent) {
accept = true;
score = SCORE_BETTER;
} else if (!tabuHit) {
// 模拟退火:接受劣解
double delta = current.getFitness() - repaired.getFitness(); // 负值表示更差
double acceptProb = Math.exp(delta / temperature);
accept = rnd.nextDouble() < acceptProb;
score = accept ? SCORE_ACCEPTED : SCORE_REJECTED;
} else {
accept = false;
score = SCORE_REJECTED;
}
updateOperatorScores(destroyIdx, repairIdx, score, true);
if (accept) {
current = lightCopy(repaired);
if (betterThanBest) {
best = lightCopy(repaired);
improveCount++;
double delta = best.getFitness() - currentBestFitness;
if (delta > MINOR_IMPROVEMENT_THRESHOLD) {
noImprovementCount = 0;
currentBestFitness = best.getFitness();
if (delta > SIGNIFICANT_IMPROVEMENT_THRESHOLD) {
FileHelper.writeLogFile(String.format(
"ALNS - 找到更好解(显著), 迭代=%d, fitness=%.12f, destroy=%s, repair=%s",
iterations, best.getFitness(), destroyOp.name, repairOp.name));
} else {
FileHelper.writeLogFile(String.format(
"ALNS - 找到更好解(微小), 迭代=%d, fitness=%.12f, delta=%.2e, destroy=%s, repair=%s",
iterations, best.getFitness(), delta, destroyOp.name, repairOp.name));
}
}
}
} else {
noImprovementCount++;
}
// ---- 7. 温度冷却 ----
temperature *= COOLING_RATE;
if (temperature < FINAL_TEMPERATURE) {
temperature = FINAL_TEMPERATURE;
}
// ---- 8. 段更新权重 ----
if ((iter + 1) % SEGMENT_SIZE == 0) {
updateWeights();
}
// ---- 9. 提前停止 ----
if (noImprovementCount >= MAX_NO_IMPROVE_ITERATIONS) {
FileHelper.writeLogFile(String.format(
"ALNS - 提前停止: 连续%d次无改进", MAX_NO_IMPROVE_ITERATIONS));
break;
}
long elapsedMs = System.currentTimeMillis() - startTimeMs;
if (elapsedMs > remainingBudgetMs) {
FileHelper.writeLogFile(String.format(
"ALNS - 提前停止: 达到时间预算(%.1fmin)", elapsedMs / 60000.0));
break;
}
// 每 10 次迭代输出一次状态
if ((iter + 1) % 10 == 0) {
FileHelper.writeLogFile(String.format(
"ALNS - 迭代%d/%d, 改进=%d, 无改进连续=%d, T=%.4f, fitness=%.12f",
iterations, maxIterations, improveCount, noImprovementCount,
temperature, best.getFitness()));
}
}
// 最终权重更新
updateWeights();
FileHelper.writeLogFile(String.format(
"ALNS - 结束: 总迭代=%d, 改进次数=%d, 最终fitness=%.12f, 最终温度=%.4f",
iterations, improveCount, best.getFitness(), temperature));
FileHelper.writeLogFile(KpiLowerBoundCalculator.generateGapReport(best));
logOperatorStats();
return best;
}
/**
* 轻量拷贝:只复制 generateNeighbor/DelOrder 需要的字段,避免全量 JSON 深拷贝导致 OOM。
* result/machines/operatRel 等重型数据共享引用(generateNeighbor 只读,不修改)。
*/
private Chromosome lightCopy(Chromosome source) {
Chromosome copy = new Chromosome();
copy.setOperationSequencing(new CopyOnWriteArrayList<>(source.getOperationSequencing()));
copy.setMachineSelection(new CopyOnWriteArrayList<>(source.getMachineSelection()));
copy.setGlobalOpList(new CopyOnWriteArrayList<>(source.getGlobalOpList()));
copy.setOrders(new CopyOnWriteArrayList<>(source.getOrders()));
copy.setAllOperations(new CopyOnWriteArrayList<>(source.getAllOperations()));
copy.setResult(source.getResult());
copy.setMachines(source.getMachines());
copy.setOperatRel(new CopyOnWriteArrayList<>(source.getOperatRel()));
copy.setScenarioID(source.getScenarioID());
copy.setBaseTime(source.getBaseTime());
copy.setGenerateType(source.getGenerateType());
copy.setFitnessLevel(source.getFitnessLevel());
copy.setFitness(source.getFitness());
return copy;
}
// ====================================================================
// Destroy 算子(破坏部分解)
// ====================================================================
/**
* Destroy 1: 随机换设备。
* 随机选择 destroyRatio 比例的工序,为其更换机器(如果有多个机器选项)。
*/
private Chromosome destroyRandomMachineChange(Chromosome c, double destroyRatioMin, double destroyRatioMax) {
List<GAScheduleResult> results = c.getResult();
if (results == null || results.isEmpty()) return c;
// 只选择有多个机器选项的工序
List<GAScheduleResult> candidates = results.stream()
.filter(r -> {
Entry e = entryByIds.get(r.getOperationId());
return e != null && e.getMachineOptions() != null && e.getMachineOptions().size() > 1;
})
.collect(Collectors.toList());
if (candidates.isEmpty()) return c;
double ratio = destroyRatioMin + rnd.nextDouble() * (destroyRatioMax - destroyRatioMin);
int destroyCount = Math.max(1, (int) (candidates.size() * ratio));
Collections.shuffle(candidates, rnd);
Chromosome copy = copyChromosome(c);
CopyOnWriteArrayList<Integer> machineSel = copy.getMachineSelection();
if (machineSel == null) return copy;
// 构建正确的 machineSelection 位置索引:groupId_sequence → globalOpList 索引
Map<String, Integer> machinePosIndex = new HashMap<>();
List<GlobalOperationInfo> globalOpList = c.getGlobalOpList();
for (int i = 0; i < globalOpList.size(); i++) {
Entry op = globalOpList.get(i).getOp();
machinePosIndex.put(op.getGroupId() + "_" + op.getSequence(), i);
}
for (int i = 0; i < Math.min(destroyCount, candidates.size()); i++) {
GAScheduleResult sr = candidates.get(i);
Entry entry = entryByIds.get(sr.getOperationId());
if (entry == null || entry.getMachineOptions() == null) continue;
List<MachineOption> options = entry.getMachineOptions();
if (options.size() <= 1) continue;
String key = entry.getGroupId() + "_" + entry.getSequence();
Integer pos = machinePosIndex.get(key);
if (pos == null || pos >= machineSel.size()) continue;
// 随机选一个不同的机器(machineSelection 存的是 1-based 序号,不是 machineId)
int currentIdx = machineSel.get(pos) - 1; // 转为 0-based
int newIdx;
if (options.size() == 2) {
newIdx = (currentIdx == 0) ? 1 : 0;
} else {
do {
newIdx = rnd.nextInt(options.size());
} while (newIdx == currentIdx);
}
machineSel.set(pos, newIdx + 1);
}
return copy;
}
/**
* Destroy 2: 瓶颈工序移除。
* 找到瓶颈设备上的工序,打乱它们在 operationSequencing 中的位置。
*/
private Chromosome destroyBottleneckOps(Chromosome c, double destroyRatioMin, double destroyRatioMax) {
List<GAScheduleResult> results = c.getResult();
if (results == null || results.isEmpty()) return c;
// 简单瓶颈识别:利用率最高的机器
Map<Long, Double> utilMap = new HashMap<>();
Map<Long, Long> timeMap = new HashMap<>();
for (GAScheduleResult r : results) {
Long mid = r.getMachineId();
utilMap.merge(mid, r.getProcessingTime(), Double::sum);
timeMap.merge(mid, (long) (r.getEndTime() - r.getStartTime()), Long::sum);
}
Long bottleneckMachineId = utilMap.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);
if (bottleneckMachineId == null) return c;
List<GAScheduleResult> bottleneckOps = results.stream()
.filter(r -> bottleneckMachineId.equals(r.getMachineId()))
.collect(Collectors.toList());
if (bottleneckOps.size() < 2) return c;
double ratio = destroyRatioMin + rnd.nextDouble() * (destroyRatioMax - destroyRatioMin);
int destroyCount = Math.max(2, (int) (bottleneckOps.size() * ratio));
Chromosome copy = copyChromosome(c);
CopyOnWriteArrayList<Integer> opSeq = copy.getOperationSequencing();
if (opSeq == null) return copy;
// 找到瓶颈工序在 opSeq 中的位置
Set<Integer> bottleneckEntryIds = bottleneckOps.stream()
.map(GAScheduleResult::getOperationId)
.collect(Collectors.toSet());
List<Integer> bottleneckPositions = new ArrayList<>();
for (int i = 0; i < opSeq.size(); i++) {
if (bottleneckEntryIds.contains(opSeq.get(i))) {
bottleneckPositions.add(i);
}
}
if (bottleneckPositions.size() < 2) return copy;
// 随机选择 destroyCount 个位置,打乱它们的值
Collections.shuffle(bottleneckPositions, rnd);
List<Integer> selectedPositions = bottleneckPositions.subList(0,
Math.min(destroyCount, bottleneckPositions.size()));
List<Integer> values = new ArrayList<>();
for (int pos : selectedPositions) {
values.add(opSeq.get(pos));
}
Collections.shuffle(values, rnd);
for (int j = 0; j < selectedPositions.size(); j++) {
opSeq.set(selectedPositions.get(j), values.get(j));
}
return copy;
}
/**
* Destroy 3: 延迟工序打乱。
* 找到导致延迟的工序,打乱它们的机器选择。
*/
private Chromosome destroyDelayOps(Chromosome c, double destroyRatioMin, double destroyRatioMax) {
List<Order> orderList = c.getOrders();
if (orderList == null || orderList.isEmpty()) return c;
// 找到延迟的订单
Set<String> delayedOrderIds = orderList.stream()
.filter(o -> o.getDelayHours() > 0)
.map(Order::getOrderId)
.collect(Collectors.toSet());
if (delayedOrderIds.isEmpty()) return c;
List<GAScheduleResult> results = c.getResult();
if (results == null || results.isEmpty()) return c;
List<GAScheduleResult> delayedResults = results.stream()
.filter(r -> delayedOrderIds.contains(r.getOrderId()))
.collect(Collectors.toList());
if (delayedResults.isEmpty()) return c;
double ratio = destroyRatioMin + rnd.nextDouble() * (destroyRatioMax - destroyRatioMin);
int destroyCount = Math.max(1, (int) (delayedResults.size() * ratio));
Collections.shuffle(delayedResults, rnd);
Chromosome copy = copyChromosome(c);
CopyOnWriteArrayList<Integer> machineSel = copy.getMachineSelection();
if (machineSel == null) return copy;
// 构建正确的 machineSelection 位置索引:groupId_sequence → globalOpList 索引
Map<String, Integer> machinePosIndex = new HashMap<>();
List<GlobalOperationInfo> globalOpList = c.getGlobalOpList();
for (int i = 0; i < globalOpList.size(); i++) {
Entry op = globalOpList.get(i).getOp();
machinePosIndex.put(op.getGroupId() + "_" + op.getSequence(), i);
}
for (int i = 0; i < Math.min(destroyCount, delayedResults.size()); i++) {
GAScheduleResult sr = delayedResults.get(i);
Entry entry = entryByIds.get(sr.getOperationId());
if (entry == null || entry.getMachineOptions() == null || entry.getMachineOptions().isEmpty())
continue;
List<MachineOption> options = entry.getMachineOptions();
if (options.size() <= 1) continue;
String key = entry.getGroupId() + "_" + entry.getSequence();
Integer pos = machinePosIndex.get(key);
if (pos == null || pos >= machineSel.size()) continue;
// 随机选一个不同的机器(machineSelection 存的是 1-based 序号,不是 machineId)
int currentIdx = machineSel.get(pos) - 1;
int newIdx;
if (options.size() == 2) {
newIdx = (currentIdx == 0) ? 1 : 0;
} else {
do {
newIdx = rnd.nextInt(options.size());
} while (newIdx == currentIdx);
}
machineSel.set(pos, newIdx + 1);
}
return copy;
}
/**
* Destroy 4: 随机区间打乱。
* 在 operationSequencing 中随机选一段连续区间,打乱其内部顺序。
*/
private Chromosome destroyRandomSegment(Chromosome c, double destroyRatioMin, double destroyRatioMax) {
CopyOnWriteArrayList<Integer> opSeq = c.getOperationSequencing();
if (opSeq == null || opSeq.size() < 4) return c;
Chromosome copy = copyChromosome(c);
CopyOnWriteArrayList<Integer> seq = copy.getOperationSequencing();
if (seq == null) return copy;
double ratio = destroyRatioMin + rnd.nextDouble() * (destroyRatioMax - destroyRatioMin);
int segmentLen = Math.max(2, (int) (seq.size() * ratio));
int start = rnd.nextInt(Math.max(1, seq.size() - segmentLen));
int end = Math.min(seq.size(), start + segmentLen);
// 打乱 [start, end) 区间
List<Integer> subList = new ArrayList<>(seq.subList(start, end));
Collections.shuffle(subList, rnd);
for (int i = start; i < end; i++) {
seq.set(i, subList.get(i - start));
}
return copy;
}
/**
* Destroy 5: 负载均衡迁移。
* 利用解码后的调度数据,找出利用率最高和最低的机器,
* 将超载机器上有多机器选项的工序迁移到低负载机器上。
*/
private Chromosome destroyLoadBalanceTransfer(Chromosome c, double destroyRatioMin, double destroyRatioMax) {
List<GAScheduleResult> results = c.getResult();
if (results == null || results.isEmpty()) return c;
// 构建每台机器的利用率数据
Map<Long, MachineUtilInfo> machineUtilMap = buildMachineUtilization(results);
if (machineUtilMap.size() < 2) return c;
// 找出超载机器(利用率 > 70%)和低负载机器(利用率 < 40%)
List<Long> overloadedMachines = new ArrayList<>();
List<Long> underloadedMachines = new ArrayList<>();
double totalSpan = 0;
for (MachineUtilInfo info : machineUtilMap.values()) {
totalSpan = Math.max(totalSpan, info.span);
}
for (Map.Entry<Long, MachineUtilInfo> entry : machineUtilMap.entrySet()) {
double util = totalSpan > 0 ? entry.getValue().span / totalSpan * 100 : 0;
if (util > 70) overloadedMachines.add(entry.getKey());
if (util < 40) underloadedMachines.add(entry.getKey());
}
if (overloadedMachines.isEmpty() || underloadedMachines.isEmpty()) return c;
// 收集超载机器上有多个机器选项的工序
Set<Long> overloadedSet = new HashSet<>(overloadedMachines);
List<GAScheduleResult> candidates = results.stream()
.filter(r -> overloadedSet.contains(r.getMachineId()))
.filter(r -> {
Entry e = entryByIds.get(r.getOperationId());
return e != null && e.getMachineOptions() != null && e.getMachineOptions().size() > 1;
})
.collect(Collectors.toList());
if (candidates.isEmpty()) return c;
double ratio = destroyRatioMin + rnd.nextDouble() * (destroyRatioMax - destroyRatioMin);
int destroyCount = Math.max(1, (int) (candidates.size() * ratio));
Collections.shuffle(candidates, rnd);
Chromosome copy = copyChromosome(c);
CopyOnWriteArrayList<Integer> machineSel = copy.getMachineSelection();
if (machineSel == null) return copy;
// 构建 machineSelection 位置索引
Map<String, Integer> machinePosIndex = new HashMap<>();
List<GlobalOperationInfo> globalOpList = c.getGlobalOpList();
for (int i = 0; i < globalOpList.size(); i++) {
Entry op = globalOpList.get(i).getOp();
machinePosIndex.put(op.getGroupId() + "_" + op.getSequence(), i);
}
// 构建低负载机器可用的 machineId 集合
Set<Long> underloadedMachineIds = new HashSet<>(underloadedMachines);
int transferred = 0;
for (int i = 0; i < Math.min(destroyCount, candidates.size()); i++) {
GAScheduleResult sr = candidates.get(i);
Entry entry = entryByIds.get(sr.getOperationId());
if (entry == null || entry.getMachineOptions() == null) continue;
List<MachineOption> options = entry.getMachineOptions();
if (options.size() <= 1) continue;
// 找到该工序可用的低负载机器选项
List<Integer> lowLoadIndices = new ArrayList<>();
for (int j = 0; j < options.size(); j++) {
if (underloadedMachineIds.contains(options.get(j).getMachineId())) {
lowLoadIndices.add(j);
}
}
if (lowLoadIndices.isEmpty()) continue;
String key = entry.getGroupId() + "_" + entry.getSequence();
Integer pos = machinePosIndex.get(key);
if (pos == null || pos >= machineSel.size()) continue;
// 随机选一个低负载机器
int newIdx = lowLoadIndices.get(rnd.nextInt(lowLoadIndices.size()));
machineSel.set(pos, newIdx + 1);
transferred++;
}
if (transferred > 0) {
FileHelper.writeLogFile(String.format(
"ALNS-负载均衡迁移: 超载机器=%d台, 低负载机器=%d台, 转移工序=%d/%d",
overloadedMachines.size(), underloadedMachines.size(), transferred, destroyCount));
}
return copy;
}
// ====================================================================
// Repair 算子(修复被破坏的解)
// ====================================================================
/**
* Repair 1: 多轮修复。
* 多次调用 VNS 的 generateNeighbor,每次解码并比较,选最优结果。
* 这比单次 generateNeighbor 更有可能找到改进。
*/
private Chromosome repairMultiPass(Chromosome c, VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines) {
Chromosome base = copyChromosome(c);
decode(decoder, base, machines);
Chromosome bestRepair = base;
double bestRepairFitness = base.getFitness();
for (int pass = 0; pass < MULTI_PASS_COUNT; pass++) {
// 基于当前最优修复结果生成邻域
Chromosome neighbor = vns.generateNeighbor(bestRepair);
if (neighbor == null) continue;
// 解码邻域
Chromosome neighborCopy = copyChromosome(neighbor);
neighborCopy.setResult(new CopyOnWriteArrayList<>());
decode(decoder, neighborCopy, machines);
if (isBetter(neighborCopy, bestRepair)) {
bestRepair = neighborCopy;
bestRepairFitness = bestRepair.getFitness();
}
}
return bestRepair;
}
/**
* Repair 2: 贪婪换设备。
* 通过 VNS 的 generateNeighbor 生成邻居后,再调用局部搜索修复。
*/
private Chromosome repairGreedyMachine(Chromosome c, VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines) {
// 通过 VNS 的瓶颈感知策略生成一个邻居
Chromosome neighbor = vns.generateNeighbor(c);
if (neighbor == null) return c;
return neighbor;
}
/**
* Repair 3: 局部搜索修复。
* 通过 VNS 的局部搜索对解进行修复优化。
*/
private Chromosome repairLocalSearch(Chromosome c, VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines) {
// 深拷贝后解码,再通过 VNS 的 generateNeighbor 生成一个邻居
Chromosome copy = copyChromosome(c);
decode(decoder, copy, machines);
Chromosome neighbor = vns.generateNeighbor(copy);
return neighbor != null ? neighbor : copy;
}
// ====================================================================
// 自适应权重管理
// ====================================================================
/**
* 轮盘赌选择算子。
*/
private int rouletteSelect(double[] weights) {
double total = 0.0;
for (double w : weights) total += w;
if (total <= 0) return rnd.nextInt(weights.length);
double rand = rnd.nextDouble() * total;
double cumulative = 0.0;
for (int i = 0; i < weights.length; i++) {
cumulative += weights[i];
if (rand <= cumulative) return i;
}
return weights.length - 1;
}
/**
* 更新算子分数(累加到此段的 score 中)。
*/
private void updateOperatorScores(int destroyIdx, int repairIdx, double score, boolean used) {
destroyScores[destroyIdx] += score;
repairScores[repairIdx] += score;
if (used) {
destroyUseCount[destroyIdx]++;
repairUseCount[repairIdx]++;
}
}
/**
* 段结束时更新权重。
* 权重 = reactionFactor * 旧权重 + (1 - reactionFactor) * (段分数 / 使用次数)
*/
private void updateWeights() {
for (int i = 0; i < destroyWeights.length; i++) {
double avgScore = destroyUseCount[i] > 0
? destroyScores[i] / destroyUseCount[i] : 0.0;
destroyWeights[i] = Math.max(MIN_WEIGHT,
REACTION_FACTOR * destroyWeights[i] + (1 - REACTION_FACTOR) * avgScore);
destroyScores[i] = 0.0;
destroyUseCount[i] = 0;
}
for (int i = 0; i < repairWeights.length; i++) {
double avgScore = repairUseCount[i] > 0
? repairScores[i] / repairUseCount[i] : 0.0;
repairWeights[i] = Math.max(MIN_WEIGHT,
REACTION_FACTOR * repairWeights[i] + (1 - REACTION_FACTOR) * avgScore);
repairScores[i] = 0.0;
repairUseCount[i] = 0;
}
}
/**
* 输出算子权重统计。
*/
private void logOperatorStats() {
StringBuilder sb = new StringBuilder("ALNS - 算子权重: ");
sb.append("Destroy[");
for (int i = 0; i < destroyOperators.size(); i++) {
sb.append(String.format("%s=%.3f", destroyOperators.get(i).name, destroyWeights[i]));
if (i < destroyOperators.size() - 1) sb.append(", ");
}
sb.append("] Repair[");
for (int i = 0; i < repairOperators.size(); i++) {
sb.append(String.format("%s=%.3f", repairOperators.get(i).name, repairWeights[i]));
if (i < repairOperators.size() - 1) sb.append(", ");
}
sb.append("]");
FileHelper.writeLogFile(sb.toString());
}
// ====================================================================
// 辅助方法(复用 VNS 风格)
// ====================================================================
/**
* 从调度结果构建每台机器的利用率数据。
*/
private Map<Long, MachineUtilInfo> buildMachineUtilization(List<GAScheduleResult> results) {
Map<Long, MachineUtilInfo> map = new HashMap<>();
for (GAScheduleResult r : results) {
Long mid = r.getMachineId();
MachineUtilInfo info = map.get(mid);
if (info == null) {
info = new MachineUtilInfo();
info.machineId = mid;
info.minStart = r.getStartTime();
info.maxEnd = r.getEndTime();
info.opCount = 1;
info.totalWork = r.getProcessingTime();
map.put(mid, info);
} else {
info.minStart = Math.min(info.minStart, r.getStartTime());
info.maxEnd = Math.max(info.maxEnd, r.getEndTime());
info.opCount++;
info.totalWork += r.getProcessingTime();
}
}
// 计算每台机器的 span
for (MachineUtilInfo info : map.values()) {
info.span = info.maxEnd - info.minStart;
}
return map;
}
private void decode(GeneticDecoder decoder, Chromosome chromosome, List<Machine> machines) {
chromosome.setResult(new CopyOnWriteArrayList<>());
if (cachedMachines == null) {
cachedMachines = ProductionDeepCopyUtil.deepCopyList(machines, Machine.class);
}
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(cachedMachines, Machine.class));
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(cachedOrders), Order.class));
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(cachedEntryRel), GroupResult.class));
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(
cachedMaterials, String.class, Material.class));
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(cachedAllOperations), Entry.class));
List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
if (lockedOrders != null && !lockedOrders.isEmpty()) {
chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
} else {
chromosome.setResultOld(new CopyOnWriteArrayList<>());
}
decoder.decodeChromosomeWithCache(chromosome, false);
}
private Chromosome copyChromosome(Chromosome c) {
Chromosome copy = new Chromosome();
if (c.getMachineSelection() != null) {
copy.setMachineSelection(new CopyOnWriteArrayList<>(c.getMachineSelection()));
}
if (c.getOperationSequencing() != null) {
copy.setOperationSequencing(new CopyOnWriteArrayList<>(c.getOperationSequencing()));
}
if (c.getObjectives() != null) {
copy.setObjectives(Arrays.copyOf(c.getObjectives(), c.getObjectives().length));
}
if (c.getGlobalOpList() != null) {
copy.setGlobalOpList(new CopyOnWriteArrayList<>(c.getGlobalOpList()));
}
copy.setMachines(c.getMachines());
copy.setOrders(c.getOrders());
copy.setOperatRel(c.getOperatRel());
copy.setMaterials(c.getMaterials());
copy.setAllOperations(c.getAllOperations());
copy.setFitness(c.getFitness());
copy.setFitnessLevel(c.getFitnessLevel());
copy.setResult(c.getResult());
copy.setScenarioID(c.getScenarioID());
copy.setBaseTime(c.getBaseTime());
return copy;
}
private boolean isBetter(Chromosome c1, Chromosome c2) {
return fitnessCalculator.isBetter(c1, c2);
}
private Map<Integer, Object> buildEntryKey() {
Map<String, Entry> entryMap = new HashMap<>();
Map<Integer, Entry> entryByIdMap = new HashMap<>();
for (Entry entry : allOperations) {
entryByIdMap.put(entry.getId(), entry);
}
Map<Integer, Object> result = new HashMap<>();
result.put(1, entryByIdMap);
return result;
}
// ====================================================================
// 内部类
// ====================================================================
/**
* 机器利用率信息(用于负载均衡计算)。
*/
private static class MachineUtilInfo {
long machineId;
double minStart;
double maxEnd;
double span;
int opCount;
double totalWork;
}
/**
* Destroy 算子:破坏解的一部分。
*/
private static class DestroyOperator {
final String name;
final DestroyFunction function;
DestroyOperator(String name, DestroyFunction function) {
this.name = name;
this.function = function;
}
Chromosome apply(Chromosome c, double ratioMin, double ratioMax) {
try {
return function.apply(c, ratioMin, ratioMax);
} catch (Exception e) {
FileHelper.writeLogFile("ALNS - Destroy算子 " + name + " 异常: " + e.getMessage());
return null;
}
}
}
@FunctionalInterface
private interface DestroyFunction {
Chromosome apply(Chromosome c, double ratioMin, double ratioMax);
}
/**
* Repair 算子:修复被破坏的解。
*/
private static class RepairOperator {
final String name;
final RepairFunction function;
RepairOperator(String name, RepairFunction function) {
this.name = name;
this.function = function;
}
Chromosome apply(Chromosome c, VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines) {
try {
return function.apply(c, vns, decoder, machines);
} catch (Exception e) {
FileHelper.writeLogFile("ALNS - Repair算子 " + name + " 异常: " + e.getMessage());
return null;
}
}
}
@FunctionalInterface
private interface RepairFunction {
Chromosome apply(Chromosome c, VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines);
}
}
\ No newline at end of file
package com.aps.service.Algorithm; package com.aps.service.Algorithm;
import com.aps.common.util.FileHelper;
import com.aps.entity.Algorithm.Chromosome; import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GlobalOperationInfo; import com.aps.entity.Algorithm.GlobalOperationInfo;
import com.aps.entity.basic.Entry; import com.aps.entity.basic.Entry;
import com.aps.entity.basic.Machine; import com.aps.entity.basic.Machine;
import com.aps.entity.basic.MachineOption; import com.aps.entity.basic.MachineOption;
import com.aps.entity.basic.Order;
import com.google.ortools.Loader; import com.google.ortools.Loader;
import com.google.ortools.sat.CpModel; import com.google.ortools.sat.CpModel;
import com.google.ortools.sat.CpSolver; import com.google.ortools.sat.CpSolver;
...@@ -16,6 +18,7 @@ import com.google.ortools.sat.LinearExpr; ...@@ -16,6 +18,7 @@ import com.google.ortools.sat.LinearExpr;
import com.google.ortools.sat.Literal; import com.google.ortools.sat.Literal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
...@@ -48,6 +51,9 @@ public class CpSatFjspModel { ...@@ -48,6 +51,9 @@ public class CpSatFjspModel {
private final List<Machine> machines; private final List<Machine> machines;
private final List<GlobalOperationInfo> globalOpList; private final List<GlobalOperationInfo> globalOpList;
private final List<Entry> allOperations; private final List<Entry> allOperations;
private final LocalDateTime baseTime;
private final List<Order> orders;
private final int operationCount; private final int operationCount;
private CpModel model; private CpModel model;
...@@ -61,11 +67,14 @@ public class CpSatFjspModel { ...@@ -61,11 +67,14 @@ public class CpSatFjspModel {
public CpSatFjspModel(List<GlobalOperationInfo> globalOpList, public CpSatFjspModel(List<GlobalOperationInfo> globalOpList,
List<Entry> allOperations, List<Entry> allOperations,
List<Machine> machines, List<Machine> machines,
List<Order> orders,
LocalDateTime baseTime) { LocalDateTime baseTime) {
this.globalOpList = globalOpList; this.globalOpList = globalOpList;
this.allOperations = allOperations; this.allOperations = allOperations;
this.machines = machines; this.machines = machines;
this.operationCount = globalOpList.size(); this.operationCount = globalOpList.size();
this.orders=orders;
this.baseTime=baseTime;
} }
private int estimateHorizon() { private int estimateHorizon() {
...@@ -221,28 +230,90 @@ public class CpSatFjspModel { ...@@ -221,28 +230,90 @@ public class CpSatFjspModel {
globalOpList.get(i).getOp().getPriority()); globalOpList.get(i).getOp().getPriority());
} }
int priorityTerms = 0; List<LinearArgument> objTerms = new ArrayList<>();
for (int i = 0; i < operationCount; i++) { List<Long> objWeights = new ArrayList<>();
if (globalOpList.get(i).getOp().getPriority() > 0) priorityTerms++;
}
LinearArgument[] objVars = new LinearArgument[1 + priorityTerms]; objTerms.add(makespanVar);
long[] objCoeffs = new long[1 + priorityTerms]; objWeights.add(100L);
objVars[0] = makespanVar;
objCoeffs[0] = 100;
int t = 1;
for (int i = 0; i < operationCount; i++) { for (int i = 0; i < operationCount; i++) {
double priority = globalOpList.get(i).getOp().getPriority(); double priority = globalOpList.get(i).getOp().getPriority();
if (priority > 0) { if (priority > 0) {
objVars[t] = endVars.get(i); objTerms.add(endVars.get(i));
objCoeffs[t] = (long)(maxPriority - priority + 1); objWeights.add((long)(maxPriority - priority + 1));
t++;
} }
} }
model.minimize(LinearExpr.weightedSum(objVars, objCoeffs));
}
// ==================== 新增目标1:Tardiness(延迟时间) ====================
// 按订单分组,找到每个订单最后一道工序的end_time
Map<Integer, List<Integer>> groupOps = new LinkedHashMap<>();
Map<Integer, Long> groupDueDates = new HashMap<>();
for (int i = 0; i < operationCount; i++) {
GlobalOperationInfo info = globalOpList.get(i);
int groupId = info.getGroupId();
groupOps.computeIfAbsent(groupId, k -> new ArrayList<>()).add(i);
if (!groupDueDates.containsKey(groupId)) {
Order order = orders.stream()
.filter(o -> info.getOp().getOrderId().equals(o.getOrderId()))
.findFirst().orElse(null);
if (order != null && order.getDueDate() != null) {
long dueMin = ChronoUnit.MINUTES.between(baseTime, order.getDueDate());
groupDueDates.put(groupId, dueMin * 60);
}
}
}
long totalTardinessWeight = 80L;
for (Map.Entry<Integer, List<Integer>> entry : groupOps.entrySet()) {
int groupId = entry.getKey();
List<Integer> opIndices = entry.getValue();
// 找到该订单最后完成的工序
IntVar lastEnd = model.newIntVar(0, horizonSeconds, "lastEnd_g" + groupId);
List<LinearArgument> endCandidates = new ArrayList<>();
for (int idx : opIndices) {
endCandidates.add(endVars.get(idx));
}
model.addMaxEquality(lastEnd, endCandidates.toArray(new LinearArgument[0]));
Long dueDateSec = groupDueDates.get(groupId);
if (dueDateSec != null) {
IntVar tardiness = model.newIntVar(0, horizonSeconds, "tardiness_g" + groupId);
LinearArgument diff = LinearExpr.sum(new LinearArgument[]{
lastEnd, LinearExpr.constant(-dueDateSec)});
model.addMaxEquality(tardiness, new LinearArgument[]{diff, LinearExpr.constant(0)});
objTerms.add(tardiness);
objWeights.add(totalTardinessWeight);
}
}
// ==================== 新增目标2:Machine Load Balance(负载均衡) ====================
// 简化版本:取每台机器上所有工序的最大end_time,加入目标函数
// 这样 CP-SAT 会倾向于让各机器的完工时间更均匀
Map<Long, List<LinearArgument>> machineEndTimes = new HashMap<>();
for (int i = 0; i < operationCount; i++) {
GlobalOperationInfo info = globalOpList.get(i);
List<MachineOption> options = info.getOp().getMachineOptions();
if (options == null || options.isEmpty()) continue;
for (MachineOption mo : options) {
machineEndTimes.computeIfAbsent(mo.getMachineId(), k -> new ArrayList<>()).add(endVars.get(i));
}
}
long loadBalanceWeight = 30L;
for (Map.Entry<Long, List<LinearArgument>> entry : machineEndTimes.entrySet()) {
IntVar machineMakespan = model.newIntVar(0, horizonSeconds, "mk_m" + entry.getKey());
model.addMaxEquality(machineMakespan, entry.getValue().toArray(new LinearArgument[0]));
objTerms.add(machineMakespan);
objWeights.add(loadBalanceWeight);
}
model.minimize(LinearExpr.weightedSum(
objTerms.toArray(new LinearArgument[0]),
objWeights.stream().mapToLong(Long::longValue).toArray()));
}
private boolean enableLogging=true;
/** /**
* 单次求解,返回一个 Chromosome * 单次求解,返回一个 Chromosome
*/ */
...@@ -258,8 +329,13 @@ public class CpSatFjspModel { ...@@ -258,8 +329,13 @@ public class CpSatFjspModel {
CpSolverStatus status = solver.solve(model); CpSolverStatus status = solver.solve(model);
if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) { if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) {
long makespanSec = (long) solver.value(makespanVar);
FileHelper.log("[CpSatFjsp] 单次求解 状态=" + status
+ " CP-SAT目标值=" + String.format("%,d", (long) solver.objectiveValue())
+ " makespan=" + String.format("%,d秒 (%.1f小时)", makespanSec, makespanSec / 3600.0),enableLogging);
return extractChromosome(solver); return extractChromosome(solver);
} }
FileHelper.log("[CpSatFjsp] 单次求解 状态=" + status + "(无解)",enableLogging);
return null; return null;
} }
...@@ -328,15 +404,22 @@ public class CpSatFjspModel { ...@@ -328,15 +404,22 @@ public class CpSatFjspModel {
CpSolverStatus status = solver.solve(model); CpSolverStatus status = solver.solve(model);
if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) { if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) {
long msSec = (long) solver.value(makespanVar);
FileHelper.log("[CpSatFjsp] 第" + (round + 1) + "轮 状态=" + status
+ " CP-SAT目标=" + String.format("%,d", (long) solver.objectiveValue())
+ " makespan=" + String.format("%,d秒(%.1f小时)", msSec, msSec / 3600.0),enableLogging);
Chromosome chromo = extractChromosome(solver); Chromosome chromo = extractChromosome(solver);
if (chromo != null && !containsDuplicate(results, chromo)) { if (chromo != null && !containsDuplicate(results, chromo)) {
chromo.setGsOrls(4); chromo.setGsOrls(4);
chromo.setGenerateType("CP-SAT"); chromo.setGenerateType("CP-SAT");
results.add(chromo); results.add(chromo);
} }
} else {
FileHelper.log("[CpSatFjsp] 第" + (round + 1) + "轮 状态=" + status + "(无解)",enableLogging);
} }
} }
FileHelper.log("[CpSatFjsp] 多样性解生成完成,共" + results.size() + "个解",enableLogging);
return results; return results;
} }
......
...@@ -93,7 +93,7 @@ public class CpSatInitializer { ...@@ -93,7 +93,7 @@ public class CpSatInitializer {
*/ */
private List<Chromosome> smallScaleSolve(List<GlobalOperationInfo> globalOpList, private List<Chromosome> smallScaleSolve(List<GlobalOperationInfo> globalOpList,
int targetCount, int timeBudgetSec) { int targetCount, int timeBudgetSec) {
CpSatFjspModel model = new CpSatFjspModel(globalOpList, allOperations, machines, baseTime); CpSatFjspModel model = new CpSatFjspModel(globalOpList, allOperations, machines,orders, baseTime);
return model.generateDiverseSolutions( return model.generateDiverseSolutions(
Math.min(targetCount, 8), timeBudgetSec, true); Math.min(targetCount, 8), timeBudgetSec, true);
} }
...@@ -103,7 +103,7 @@ public class CpSatInitializer { ...@@ -103,7 +103,7 @@ public class CpSatInitializer {
*/ */
private List<Chromosome> mediumScaleSolve(List<GlobalOperationInfo> globalOpList, private List<Chromosome> mediumScaleSolve(List<GlobalOperationInfo> globalOpList,
int targetCount, int timeBudgetSec) { int targetCount, int timeBudgetSec) {
CpSatFjspModel model = new CpSatFjspModel(globalOpList, allOperations, machines, baseTime); CpSatFjspModel model = new CpSatFjspModel(globalOpList, allOperations, machines,orders, baseTime);
int effectiveTarget = Math.min(targetCount, 5); int effectiveTarget = Math.min(targetCount, 5);
int perSolveTime = Math.max(timeBudgetSec, 15); int perSolveTime = Math.max(timeBudgetSec, 15);
return model.generateDiverseSolutions(effectiveTarget, perSolveTime, true); return model.generateDiverseSolutions(effectiveTarget, perSolveTime, true);
...@@ -158,7 +158,7 @@ public class CpSatInitializer { ...@@ -158,7 +158,7 @@ public class CpSatInitializer {
} }
CpSatFjspModel model = new CpSatFjspModel( CpSatFjspModel model = new CpSatFjspModel(
bottleneckOps, allOperations, bottleneckMachines, baseTime); bottleneckOps, allOperations, bottleneckMachines,orders, baseTime);
List<Chromosome> cpSatResults = model.generateDiverseSolutions( List<Chromosome> cpSatResults = model.generateDiverseSolutions(
effectiveTarget, timeBudgetSec, true); effectiveTarget, timeBudgetSec, true);
......
package com.aps.service.Algorithm;
import com.aps.common.util.FileHelper;
import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.GlobalOperationInfo;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.Algorithm.ObjectiveWeights;
import com.aps.entity.basic.*;
import com.google.ortools.Loader;
import com.google.ortools.sat.CpModel;
import com.google.ortools.sat.CpSolver;
import com.google.ortools.sat.CpSolverStatus;
import com.google.ortools.sat.IntVar;
import com.google.ortools.sat.IntervalVar;
import com.google.ortools.sat.LinearArgument;
import com.google.ortools.sat.LinearExpr;
import com.google.ortools.sat.Literal;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* LNS (Large Neighborhood Search) + OR-Tools CP-SAT
*
* 从 NSGA-II 帕累托前沿取一个染色体:
* 1. 随机"释放" 10%~30% 的工序(让 CP-SAT 重排)
* 2. 其余工序"冻结"(保持机器和时间不变)
* 3. CP-SAT 在 5~15 秒内给出一个更好的调度
* 4. decode 后若目标改进则接受,否则回退
*
* 循环多轮。
*/
public class CpSatLnsNeighborhood {
private static boolean NATIVE_LIBRARY_LOADED = false;
static {
try {
Loader.loadNativeLibraries();
Class.forName("com.google.ortools.sat.CpModel");
NATIVE_LIBRARY_LOADED = true;
FileHelper.writeLogFile("[CpSatLns] OR-Tools 已加载");
} catch (Throwable t) {
FileHelper.writeLogFile("[CpSatLns] OR-Tools 本机库不可用,LNS 被禁用:" + t.getMessage());
}
}
private final List<Entry> allOperations;
private final List<Machine> machines;
private final FitnessCalculator fitnessCalculator;
private GeneticOperations geneticOperations;
private List<Machine> cachedMachines;
private List<Order> cachedOrders;
private List<GroupResult> cachedEntryRel;
private TreeMap<String, Material> cachedMaterials;
private List<Entry> cachedAllOperations;
private final Random random = new Random(20260618L);
/**
* @param allOperations 全工序列表
* @param machines 全机器列表
* @param fitnessCalculator 适应度计算器(与 HybridAlgorithm 一致)
*/
public CpSatLnsNeighborhood(List<Entry> allOperations, List<Machine> machines,List<Order> orders,
TreeMap<String, Material> materials,List<GroupResult> entryRel,
FitnessCalculator fitnessCalculator) {
this.allOperations = allOperations;
this.machines = machines;
this.fitnessCalculator = fitnessCalculator;
geneticOperations=new GeneticOperations();
// 预缓存解码需要的深拷贝列表,避免重复拷贝
cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class);
cachedOrders = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class);
cachedEntryRel = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(entryRel), GroupResult.class);
cachedMaterials = ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class);
}
/**
* 对帕累托前沿做 LNS 重优化。
* 返回的 list 包含:改进后的前沿 + 原始前沿(让下游的帕累托归并去重)
*/
public List<Chromosome> runLnsOnParetoFront(List<Chromosome> paretoFront, GeneticDecoder sharedDecoder) {
if (!NATIVE_LIBRARY_LOADED || paretoFront == null || paretoFront.isEmpty()) return paretoFront;
int totalOps = allOperations.size();
int lnsOuterRounds = Math.min(paretoFront.size(), 8);
int lnsInnerRounds = Math.max(3, Math.min(15, 8000 / Math.max(100, totalOps)));
int releasedOps = Math.max(50, (int)(totalOps * 0.15));
int cpsatTimeSeconds = Math.max(5, Math.min(15, 12000 / Math.max(100, totalOps)));
if(totalOps<50)
{
return null;
}
FileHelper.writeLogFile("[CpSatLns] 开始 LNS 重优化:工序=" + totalOps
+ ",每轮释放=" + releasedOps + ",CP-SAT=" + cpsatTimeSeconds + "s,"
+ "前沿解数=" + lnsOuterRounds + ",每解迭代=" + lnsInnerRounds);
List<Chromosome> newFront = new ArrayList<>();
int totalAttempts = 0, totalImproves = 0;
for (int i = 0; i < lnsOuterRounds && i < paretoFront.size(); i++) {
Chromosome current = lightCopy(paretoFront.get(i));
Chromosome beat = ProductionDeepCopyUtil.deepCopy(paretoFront.get(i), Chromosome.class);
// sharedDecoder.serialDecode(current);
double currentFitness = current.getFitness();
double[] curObj = current.getObjectives();
FileHelper.log("[CpSatLns] 帕累托前沿第" + (i + 1) + "个解 → 适应度="
+ String.format("%.4f", currentFitness)
+ (curObj != null ? " makespan=" + String.format("%.1f", curObj[0]) : ""),true);
for (int round = 0; round < lnsInnerRounds; round++) {
totalAttempts++;
Chromosome neighbor = optimizeNeighborhood(current, releasedOps, cpsatTimeSeconds);
if (neighbor == null) break;
decode(sharedDecoder,neighbor,machines);
if (fitnessCalculator.isBetter(neighbor, current)) {
double[] nObj = neighbor.getObjectives();
FileHelper.log("[CpSatLns] 第" + (round + 1) + "轮 ✅接受"
+ " 新适应度=" + String.format("%.4f", neighbor.getFitness())
+ " 旧适应度=" + String.format("%.4f", currentFitness)
+ (nObj != null ? " 新makespan=" + String.format("%.1f", nObj[0]) : ""),true);
current = neighbor;
beat = ProductionDeepCopyUtil.deepCopy(neighbor, Chromosome.class);
currentFitness = neighbor.getFitness();
totalImproves++;
}
}
newFront.add(beat);
}
newFront.addAll(paretoFront);
FileHelper.log("[CpSatLns] 结束:尝试=" + totalAttempts
+ ",改进=" + totalImproves + ",合并后解数=" + newFront.size(),true);
return newFront;
}
// ----------------------------------------------------------------
// 单轮邻域重优化
// ----------------------------------------------------------------
public Chromosome optimizeNeighborhood(Chromosome base, int releaseCount, int timeLimitSec) {
List<GlobalOperationInfo> globalOpList = base.getGlobalOpList();
if (globalOpList == null || globalOpList.isEmpty()) return null;
CopyOnWriteArrayList<Integer> ms = base.getMachineSelection();
if (ms == null) return null;
int totalCount = globalOpList.size();
releaseCount = Math.min(releaseCount, totalCount);
long horizon = 30L * 24 * 60 * 60; // 30 天(秒)
// ================== 1. 从 Result 中读取每道工序的机器+时间 ==================
Map<Integer, GAScheduleResult> scheduleMap = new HashMap<>();
if (base.getResult() != null) {
for (GAScheduleResult r : base.getResult()) {
int key = (r.getGroupId() * 1000000) + r.getSeq();
scheduleMap.put(key, r);
}
}
long[] opStartSec = new long[totalCount];
long[] opDurationSec = new long[totalCount];
long[] opMachineId = new long[totalCount];
for (int i = 0; i < totalCount; i++) {
GlobalOperationInfo info = globalOpList.get(i);
Entry op = info.getOp();
int key = (info.getGroupId() * 1000000) + info.getSequence();
GAScheduleResult res = scheduleMap.get(key);
if (res != null) {
opStartSec[i] = res.getStartTime();
opMachineId[i] = res.getMachineId();
opDurationSec[i] = Math.max(60L, (long)(res.getProcessingTime() * 60));
} else {
opDurationSec[i] = Math.max(60L, (long)(op.getMinProcessingTime() * 60));
opStartSec[i] = 0;
List<MachineOption> options = op.getMachineOptions();
opMachineId[i] = (options != null && !options.isEmpty()) ? options.get(0).getMachineId() : -1;
}
}
// ================== 2. 随机选"释放"的工序 ==================
BitSet released = new BitSet(totalCount);
if (releaseCount >= totalCount) {
released.set(0, totalCount);
} else {
List<Integer> indices = IntStream.range(0, totalCount)
.boxed().collect(Collectors.toList());
Collections.shuffle(indices, random);
for (int k = 0; k < releaseCount; k++) released.set(indices.get(k));
}
// ================== 3. 构建 CP-SAT 模型 ==================
try {
CpModel model = new CpModel();
Map<Long, Integer> machineIdx = new HashMap<>();
for (int m = 0; m < machines.size(); m++) machineIdx.put(machines.get(m).getId(), m);
List<List<IntervalVar>> machineIntervals = new ArrayList<>();
for (int m = 0; m < machines.size(); m++) machineIntervals.add(new ArrayList<>());
IntVar[] startVars = new IntVar[totalCount];
// presence[i][j] = 第 i 个释放工序选第 j 台机器的布尔
List<Literal[]> presenceMatrix = new ArrayList<>();
// 处理释放工序
for (int i = 0; i < totalCount; i++) {
List<MachineOption> options = globalOpList.get(i).getOp().getMachineOptions();
presenceMatrix.add(null);
if (!released.get(i)) continue;
if (options == null || options.isEmpty()) continue;
long baseStart = Math.max(0, opStartSec[i]);
long lower = Math.max(0, baseStart - 3600);
long upper = Math.min(horizon, baseStart + 10800);
if (upper <= lower) upper = lower + 3600;
startVars[i] = model.newIntVar(lower, upper, "start_" + i);
List<Literal> presList = new ArrayList<>();
for (int j = 0; j < options.size(); j++) {
MachineOption mo = options.get(j);
long procSec = Math.max(60L, (long)(mo.getProcessingTime() * 60));
Literal pres = model.newBoolVar("pres_" + i + "_" + j);
presList.add(pres);
IntVar endVar = model.newIntVar(lower + procSec, upper + procSec,
"end_" + i + "_" + j);
model.addEquality(LinearExpr.sum(new LinearArgument[]{
startVars[i], LinearExpr.constant(procSec)}), endVar);
IntervalVar iv = model.newOptionalIntervalVar(
startVars[i], model.newConstant(procSec), endVar, pres,
"iv_" + i + "_m" + mo.getMachineId());
int mIdx = machineIdx.getOrDefault(mo.getMachineId(), -1);
if (mIdx >= 0) machineIntervals.get(mIdx).add(iv);
}
Literal[] arr = presList.toArray(new Literal[0]);
if (arr.length > 0) {
model.addExactlyOne(arr);
presenceMatrix.set(i, arr);
}
}
// 冻结工序:固定 interval
for (int i = 0; i < totalCount; i++) {
if (released.get(i)) continue;
long start = Math.max(0, opStartSec[i]);
long dur = Math.max(60L, opDurationSec[i]);
long mId = opMachineId[i];
int mIdx = machineIdx.getOrDefault(mId, -1);
if (mIdx >= 0) {
IntervalVar iv = model.newFixedInterval(start, dur, "frozen_" + i);
machineIntervals.get(mIdx).add(iv);
}
}
// ================== 4. 同订单工序顺序 ==================
Map<Integer, List<Integer>> orderOpsMap = new LinkedHashMap<>();
for (int i = 0; i < totalCount; i++) {
orderOpsMap.computeIfAbsent(
globalOpList.get(i).getGroupId(), k -> new ArrayList<>()).add(i);
}
for (List<Integer> ops : orderOpsMap.values()) {
ops.sort(Comparator.comparingInt(idx -> globalOpList.get(idx).getSequence()));
for (int k = 0; k < ops.size() - 1; k++) {
int prev = ops.get(k);
int next = ops.get(k + 1);
long prevDuration = Math.max(60L, opDurationSec[prev]);
LinearArgument prevEnd;
LinearArgument nextStart;
if (released.get(prev)) {
prevEnd = LinearExpr.sum(new LinearArgument[]{
startVars[prev], LinearExpr.constant(prevDuration)});
} else {
prevEnd = LinearExpr.constant(opStartSec[prev] + prevDuration);
}
if (released.get(next)) {
nextStart = startVars[next];
} else {
nextStart = LinearExpr.constant(opStartSec[next]);
}
model.addLessOrEqual(prevEnd, nextStart);
}
}
// ================== 5. 每台机器 no_overlap ==================
for (List<IntervalVar> list : machineIntervals) {
if (list.size() > 1) {
model.addNoOverlap(list.toArray(new IntervalVar[0]));
}
}
// ================== 6. 目标:makespan + 优先级加权 + 延迟 + 负载均衡 ==================
IntVar makespan = model.newIntVar(0, horizon, "makespan");
List<LinearArgument> objTerms = new ArrayList<>();
List<Long> objWeights = new ArrayList<>();
objTerms.add(makespan);
objWeights.add(100L);
double maxPriority = 1.0;
for (int i = 0; i < totalCount; i++) {
maxPriority = Math.max(maxPriority, globalOpList.get(i).getOp().getPriority());
}
for (int i = 0; i < totalCount; i++) {
long dur = Math.max(60L, opDurationSec[i]);
LinearArgument endVar;
if (released.get(i)) {
endVar = LinearExpr.sum(new LinearArgument[]{
startVars[i], LinearExpr.constant(dur)});
} else {
endVar = LinearExpr.constant(opStartSec[i] + dur);
}
model.addLessOrEqual(endVar, makespan);
if (released.get(i)) {
double priority = globalOpList.get(i).getOp().getPriority();
long weight = (long)(maxPriority - priority + 1);
objTerms.add(endVar);
objWeights.add(weight);
}
}
// 延迟时间(只对释放的工序所属订单)
long tardinessWeight = 80L;
Map<Integer, List<LinearArgument>> groupEnds = new HashMap<>();
for (int i = 0; i < totalCount; i++) {
if (!released.get(i)) continue;
GlobalOperationInfo info = globalOpList.get(i);
groupEnds.computeIfAbsent(info.getGroupId(), k -> new ArrayList<>())
.add(LinearExpr.sum(new LinearArgument[]{
startVars[i], LinearExpr.constant(Math.max(60L, opDurationSec[i]))}));
}
for (Map.Entry<Integer, List<LinearArgument>> entry : groupEnds.entrySet()) {
IntVar lastEnd = model.newIntVar(0, horizon, "lastEnd_g" + entry.getKey());
model.addMaxEquality(lastEnd, entry.getValue().toArray(new LinearArgument[0]));
// 简化:延迟作为惩罚项加入(实际可加 due_date 判断)
objTerms.add(lastEnd);
objWeights.add(tardinessWeight);
}
// 机器负载均衡
long loadWeight = 30L;
Map<Long, List<LinearArgument>> machineEnds = new HashMap<>();
for (int i = 0; i < totalCount; i++) {
if (!released.get(i)) continue;
GlobalOperationInfo info = globalOpList.get(i);
List<com.aps.entity.basic.MachineOption> options = info.getOp().getMachineOptions();
if (options == null || options.isEmpty()) continue;
LinearArgument endVar = LinearExpr.sum(new LinearArgument[]{
startVars[i], LinearExpr.constant(Math.max(60L, opDurationSec[i]))});
for (com.aps.entity.basic.MachineOption mo : options) {
machineEnds.computeIfAbsent(mo.getMachineId(), k -> new ArrayList<>()).add(endVar);
}
}
for (Map.Entry<Long, List<LinearArgument>> entry : machineEnds.entrySet()) {
IntVar machineMk = model.newIntVar(0, horizon, "mk_m" + entry.getKey());
model.addMaxEquality(machineMk, entry.getValue().toArray(new LinearArgument[0]));
objTerms.add(machineMk);
objWeights.add(loadWeight);
}
model.minimize(LinearExpr.weightedSum(
objTerms.toArray(new LinearArgument[0]),
objWeights.stream().mapToLong(Long::longValue).toArray()));
// ================== 7. 求解 ==================
CpSolver solver = new CpSolver();
solver.getParameters().setMaxTimeInSeconds(timeLimitSec);
solver.getParameters().setNumSearchWorkers(4);
solver.getParameters().setLogSearchProgress(false);
CpSolverStatus status = solver.solve(model);
if (status != CpSolverStatus.OPTIMAL && status != CpSolverStatus.FEASIBLE) {
FileHelper.writeLogFile("[CpSatLns] 邻域求解 状态=" + status + "(无解),释放=" + releaseCount + "道工序");
return null;
}
long cpObjective = (long) solver.objectiveValue();
long cpMakespan = (long) solver.value(makespan);
FileHelper.writeLogFile("[CpSatLns] 邻域求解 状态=" + status
+ " 释放工序=" + releaseCount
+ " CP-SAT目标=" + String.format("%,d", cpObjective)
+ " makespan=" + String.format("%,d秒(%.1f小时)", cpMakespan, cpMakespan / 3600.0));
// ================== 8. 写回新染色体的 machineSelection ==================
Chromosome neighbor = base.deepCopy();
CopyOnWriteArrayList<Integer> newMs = new CopyOnWriteArrayList<>(
neighbor.getMachineSelection() != null
? neighbor.getMachineSelection()
: new ArrayList<>()
);
while (newMs.size() < totalCount) newMs.add(1);
for (int i = 0; i < totalCount; i++) {
if (!released.get(i)) continue;
Literal[] arr = presenceMatrix.get(i);
if (arr == null) continue;
for (int j = 0; j < arr.length; j++) {
if (solver.value(arr[j]) == 1L) {
newMs.set(i, j + 1); // 1-based
break;
}
}
}
neighbor.setMachineSelection(newMs);
neighbor.setGenerateType("LNS-CPSAT");
neighbor.setGsOrls(5);
return neighbor;
} catch (Exception e) {
FileHelper.writeLogFile("[CpSatLns] 邻域重优化异常:" + e.getMessage());
return null;
}
}
/**
* 轻量拷贝:只复制 generateNeighbor/DelOrder 需要的字段,避免全量 JSON 深拷贝导致 OOM。
* result/machines/operatRel 等重型数据共享引用(generateNeighbor 只读,不修改)。
*/
private Chromosome lightCopy(Chromosome source) {
Chromosome copy = new Chromosome();
copy.setOperationSequencing(new CopyOnWriteArrayList<>(source.getOperationSequencing()));
copy.setMachineSelection(new CopyOnWriteArrayList<>(source.getMachineSelection()));
copy.setGlobalOpList(new CopyOnWriteArrayList<>(source.getGlobalOpList()));
copy.setOrders(new CopyOnWriteArrayList<>(source.getOrders()));
copy.setAllOperations(new CopyOnWriteArrayList<>(source.getAllOperations()));
copy.setResult(source.getResult());
copy.setMachines(source.getMachines());
copy.setOperatRel(new CopyOnWriteArrayList<>(source.getOperatRel()));
copy.setScenarioID(source.getScenarioID());
copy.setBaseTime(source.getBaseTime());
copy.setGenerateType(source.getGenerateType());
copy.setFitnessLevel(source.getFitnessLevel());
copy.setFitness(source.getFitness());
copy.setObjectives(source.getObjectives());
geneticOperations.DelOrder(copy);
return copy;
}
/**
* 解码染色体
*/
private void decode(GeneticDecoder decoder, Chromosome chromosome , List<Machine> machines) {
// MS 校验:解码前检查 machineSelection 与 machineOptions 是否匹配
List<GlobalOperationInfo> gops = chromosome.getGlobalOpList();
List<Integer> msCheck = chromosome.getMachineSelection();
if (gops != null && msCheck != null) {
int msErrors = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Math.min(gops.size(), msCheck.size()); i++) {
Entry op = gops.get(i).getOp();
int msVal = msCheck.get(i);
if (op != null && op.getMachineOptions() != null
&& (msVal < 1 || msVal > op.getMachineOptions().size())) {
msErrors++;
if (msErrors <= 3) {
sb.append(String.format(" [idx=%d 订单%d工序%d ms=%d range=1-%d]",
i, op.getGroupId(), op.getSequence(), msVal, op.getMachineOptions().size()));
}
}
}
if (msErrors > 0) {
FileHelper.log(String.format("decode-MS校验失败: 共%d处越界 %s", msErrors, sb.toString()));
}
}
chromosome.setResult(new CopyOnWriteArrayList<>());
// 使用缓存的列表,避免重复深拷贝
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines, Machine.class));
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedOrders), Order.class));
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedEntryRel), GroupResult.class));
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(cachedMaterials, String.class, Material.class));
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class));
// 加载锁定工单到ResultOld
List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
if (lockedOrders != null && !lockedOrders.isEmpty()) {
chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
} else {
chromosome.setResultOld(new CopyOnWriteArrayList<>());
}
decoder.decodeChromosomeWithCache(chromosome,false);
}
}
...@@ -695,11 +695,6 @@ public class GeneticDecoder { ...@@ -695,11 +695,6 @@ public class GeneticDecoder {
int scheduledCount = orderProcessCounter.get(groupId); int scheduledCount = orderProcessCounter.get(groupId);
if(groupId==7)
{
int k=0;
}
List<Entry> orderOps=new ArrayList<>(); List<Entry> orderOps=new ArrayList<>();
boolean orderIsJit=orderDueDate.get(groupId)>0; boolean orderIsJit=orderDueDate.get(groupId)>0;
...@@ -743,7 +738,7 @@ public class GeneticDecoder { ...@@ -743,7 +738,7 @@ public class GeneticDecoder {
} else { } else {
orderAnchor = bom.computeSemiFinishedAnchor(this, groupId, entrysBygroupId, orderAnchor = bom.computeSemiFinishedAnchor(this, groupId, entrysBygroupId,
opMachineKeyMap, chromosome, opMachineKeyMap, chromosome,
scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById,_globalParam.isIsCheckMp()); scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById,_globalParam.isIsCheckMp(),null);
if (orderAnchor < 0) { if (orderAnchor < 0) {
orderIsJit = false; orderIsJit = false;
orderSchedulingInfo.put(groupId, orderSchedulingInfo.put(groupId,
...@@ -3524,6 +3519,7 @@ if(geneDetails!=null&&geneDetails.size()>0) ...@@ -3524,6 +3519,7 @@ if(geneDetails!=null&&geneDetails.size()>0)
private void calculateScheduleResult(Chromosome chromosome) { private void calculateScheduleResult(Chromosome chromosome) {
double[] Objectives = new double[_globalParam.getObjectiveWeights().size()]; double[] Objectives = new double[_globalParam.getObjectiveWeights().size()];
double[] weights = new double[_globalParam.getObjectiveWeights().size()];
int i = 0; int i = 0;
for (ObjectiveConfig config : _globalParam.getObjectiveConfigs()) { for (ObjectiveConfig config : _globalParam.getObjectiveConfigs()) {
...@@ -3535,7 +3531,9 @@ if(geneDetails!=null&&geneDetails.size()>0) ...@@ -3535,7 +3531,9 @@ if(geneDetails!=null&&geneDetails.size()>0)
.max() .max()
.orElse(0); .orElse(0);
Objectives[i] = makespan; Objectives[i] = makespan;
weights[i] = config.getWeight();
chromosome.setMakespan(makespan); chromosome.setMakespan(makespan);
} }
if (GlobalParam.OBJECTIVE_TARDINESS.equals(config.getName())) { if (GlobalParam.OBJECTIVE_TARDINESS.equals(config.getName())) {
// 2. 交付期满足情况(最小化延迟) // 2. 交付期满足情况(最小化延迟)
...@@ -3604,6 +3602,16 @@ if(geneDetails!=null&&geneDetails.size()>0) ...@@ -3604,6 +3602,16 @@ if(geneDetails!=null&&geneDetails.size()>0)
} }
chromosome.setObjectives(Objectives); chromosome.setObjectives(Objectives);
// 计算各 KPI 的理论下界(用于计算 Gap = (current - lowerBound) / lowerBound)
try {
KpiLowerBoundCalculator.computeAndSetLowerBounds(chromosome, _globalParam);
} catch (Exception e) {
// 下界计算异常不应影响主流程
com.aps.common.util.FileHelper.writeLogFile(
"KPI 下界计算异常: " + e.getClass().getSimpleName() + " - " + e.getMessage());
}
FitnessCalculator fitnessCalculator = new FitnessCalculator(); FitnessCalculator fitnessCalculator = new FitnessCalculator();
chromosome.setFitnessLevel(fitnessCalculator.calculateFitness(chromosome, _globalParam)); chromosome.setFitnessLevel(fitnessCalculator.calculateFitness(chromosome, _globalParam));
......
...@@ -76,7 +76,7 @@ public class HillClimbing { ...@@ -76,7 +76,7 @@ public class HillClimbing {
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class); Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class); Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
decoder.DelOrder(current);
// 构建位置索引映射:groupId_sequence -> position // 构建位置索引映射:groupId_sequence -> position
Map<String, Integer> positionIndex = buildPositionIndex(current); Map<String, Integer> positionIndex = buildPositionIndex(current);
...@@ -119,7 +119,7 @@ public class HillClimbing { ...@@ -119,7 +119,7 @@ public class HillClimbing {
positionIndex = buildPositionIndex(current); positionIndex = buildPositionIndex(current);
entryIndex = buildEntryIndex(current, entrys); entryIndex = buildEntryIndex(current, entrys);
MachinePositionIndex = buildEntryMachinePositionIndex(current); MachinePositionIndex = buildEntryMachinePositionIndex(current);
decoder.DelOrder(current); // decoder.DelOrder(current);
break; break;
} }
} }
......
...@@ -49,6 +49,8 @@ public class HybridAlgorithm { ...@@ -49,6 +49,8 @@ public class HybridAlgorithm {
private String sceneId; private String sceneId;
private VariableNeighborhoodSearch _vns; private VariableNeighborhoodSearch _vns;
private AdaptiveLargeNeighborhoodSearch _ALNS;
// 初始化算法实例 // 初始化算法实例
private HillClimbing _hillClimbing; private HillClimbing _hillClimbing;
private SimulatedAnnealing _simulatedAnnealing; private SimulatedAnnealing _simulatedAnnealing;
...@@ -116,6 +118,7 @@ public class HybridAlgorithm { ...@@ -116,6 +118,7 @@ public class HybridAlgorithm {
// 初始化变邻域搜索 // 初始化变邻域搜索
_vns = new VariableNeighborhoodSearch( allOperations,orders,materials,_entryRel, _fitnessCalculator ); _vns = new VariableNeighborhoodSearch( allOperations,orders,materials,_entryRel, _fitnessCalculator );
_vns.initMachineSelectFrequency(); _vns.initMachineSelectFrequency();
_ALNS = new AdaptiveLargeNeighborhoodSearch( allOperations,orders,materials,_entryRel, _fitnessCalculator );
_hillClimbing = new HillClimbing(allOperations,orders,materials,_entryRel, _fitnessCalculator); _hillClimbing = new HillClimbing(allOperations,orders,materials,_entryRel, _fitnessCalculator);
_simulatedAnnealing = new SimulatedAnnealing( allOperations,orders,materials,_entryRel, _fitnessCalculator); _simulatedAnnealing = new SimulatedAnnealing( allOperations,orders,materials,_entryRel, _fitnessCalculator);
_tabuSearch = new TabuSearch(allOperations,orders,materials,_entryRel, _fitnessCalculator); _tabuSearch = new TabuSearch(allOperations,orders,materials,_entryRel, _fitnessCalculator);
...@@ -148,6 +151,26 @@ public class HybridAlgorithm { ...@@ -148,6 +151,26 @@ public class HybridAlgorithm {
if (population == null || population.isEmpty()) { if (population == null || population.isEmpty()) {
throw new RuntimeException("初始种群为空,请检查 populationSize、种群初始化和解码结果"); throw new RuntimeException("初始种群为空,请检查 populationSize、种群初始化和解码结果");
} }
if (_GlobalParam.isOptimizer()) {
FileHelper.writeLogFile("LNS-CPSAT 邻域重优化-----------开始-------种群=" + population.size());
try {
CpSatLnsNeighborhood lns = new CpSatLnsNeighborhood(
allOperations, machines,orders,materials,_entryRel,_fitnessCalculator);
List<Chromosome> lnsResult = lns.runLnsOnParetoFront(population, sharedDecoder);
if (lnsResult != null && !lnsResult.isEmpty()) {
// Chromosomedecode(sharedDecoder, param, allOperations, globalOpList, lnsResult);
population = chromosomeDistinctByObjectives(lnsResult);
if (population == null || population.isEmpty()) {
population = lnsResult;
}
}
} catch (Throwable t) {
FileHelper.writeLogFile("LNS-CPSAT 失败,跳过:" + t.getMessage());
}
FileHelper.writeLogFile("LNS-CPSAT 邻域重优化-----------结束-------种群="
+ (population == null ? 0 : population.size()));
}
// if(1==1) // if(1==1)
// return getBestChromosome(population.get(0), param.getBaseTime(), starttime); // return getBestChromosome(population.get(0), param.getBaseTime(), starttime);
// 步骤2:对初始种群进行爬山法局部优化 // 步骤2:对初始种群进行爬山法局部优化
...@@ -178,9 +201,9 @@ public class HybridAlgorithm { ...@@ -178,9 +201,9 @@ public class HybridAlgorithm {
return getBestChromosome(saHcOptimized, param.getBaseTime(), starttime); return getBestChromosome(saHcOptimized, param.getBaseTime(), starttime);
} }
if(opcount>=800 ) { if(opcount>800&&opcount<2000 ) {
Chromosome best=population.get(0); Chromosome best=population.get(0);
best = _ALNS.search(best,_tabuSearch,_vns, sharedDecoder, machines);
best = _simulatedAnnealing.search(best, _tabuSearch, _vns, sharedDecoder, machines); best = _simulatedAnnealing.search(best, _tabuSearch, _vns, sharedDecoder, machines);
best = _vns.search(best,_tabuSearch, sharedDecoder, machines); best = _vns.search(best,_tabuSearch, sharedDecoder, machines);
...@@ -189,6 +212,29 @@ public class HybridAlgorithm { ...@@ -189,6 +212,29 @@ public class HybridAlgorithm {
return getBestChromosome(best, param.getBaseTime(), starttime); return getBestChromosome(best, param.getBaseTime(), starttime);
}else {
Chromosome best=population.get(0);
int topN = Math.min(3, population.size());
for (int iter = 0; iter < topN; iter++) {
FileHelper.writeLogFile("迭代进化------"+iter+"-----开始-------");
System.gc();
Chromosome chromosome=population.get(iter);
chromosome = _ALNS.search(chromosome,_tabuSearch,_vns, sharedDecoder, machines);
chromosome = _simulatedAnnealing.search(chromosome, _tabuSearch, _vns, sharedDecoder, machines);
chromosome = _vns.search(chromosome,_tabuSearch, sharedDecoder, machines);
if(_fitnessCalculator.isBetter(chromosome,best)) {
FileHelper.writeLogFile("迭代进化------发现更优解-----------");
writeKpi(chromosome);
best= chromosome;
}
FileHelper.writeLogFile("迭代进化------"+iter+"-----结束-------");
// 周期性 GC 释放内存
System.gc();
}
} }
...@@ -514,7 +560,47 @@ public class HybridAlgorithm { ...@@ -514,7 +560,47 @@ public class HybridAlgorithm {
// } // }
} }
private void writeKpi(Chromosome chromosome) {
String fitness = "";
double[] fitness1 = chromosome.getFitnessLevel();
if (fitness1 != null) {
for (int i = 0; i < fitness1.length; i++) {
fitness += fitness1[i] + ",";
}
} else {
fitness = "null (未计算)";
}
log(String.format("变邻域搜索 - kpi:%s", fitness),true);
if(chromosome.getMakespan()!=0) {
log(String.format("变邻域搜索 - kpi-Makespan: %f", chromosome.getMakespan()));
}
if(chromosome.getDelayTime()!=0) {
log(String.format("变邻域搜索 - kpi-DelayTime: %f", chromosome.getDelayTime()));
}
if(chromosome.getTotalChangeoverTime()!=0) {
log(String.format("变邻域搜索 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime()));
}
if(chromosome.getMachineLoadStd()!=0) {
log(String.format("变邻域搜索 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd()));
}
if(chromosome.getTotalFlowTime()!=0) {
log(String.format("变邻域搜索 - kpi-FlowTime: %f",chromosome.getTotalFlowTime()));
}
// ==================== 打印各 KPI 的 Gap ====================
log(KpiLowerBoundCalculator.generateGapReport(chromosome));
}
private void log(String message) {
log( message, true);
}
private void log(String message, boolean enableLogging) {
if (enableLogging ) {
FileHelper.writeLogFile(message);
}
}
} }
...@@ -1137,9 +1137,15 @@ public class Initialization { ...@@ -1137,9 +1137,15 @@ public class Initialization {
List<Chromosome> heuristicPopulation = List<Chromosome> heuristicPopulation =
generateHeuristicInitialPopulation(subParam,remaining); generateHeuristicInitialPopulation(subParam,remaining);
for (Chromosome chromo : heuristicPopulation) { for (Chromosome chromo : heuristicPopulation) {
chromo.setOrders(new CopyOnWriteArrayList<>(orders));
if(chromo!=null)
{
chromo.setOrders(new CopyOnWriteArrayList<>(orders));
population.add(chromo);
}
} }
population.addAll(heuristicPopulation);
} }
long cpSatCount = population.stream() long cpSatCount = population.stream()
......
package com.aps.service.Algorithm;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.Algorithm.IDAndChildID.NodeInfo;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.GlobalParam;
import com.aps.entity.basic.ObjectiveConfig;
import java.util.*;
import java.util.stream.Collectors;
/**
* 计算各 KPI 的理论下界(Lower Bound)。
*
* <p>Gap = (current - lowerBound) / lowerBound(lowerBound > 0 时)
* <p>当 lowerBound = 0 时(如 Tardiness),Gap = current(表示偏离 0 的绝对量)
*
* <p>各维度的理论下界:
* <ul>
* <li>Makespan(最大完工时间):DAG 关键路径长度(拓扑序 + 动态规划)</li>
* <li>FlowTime(总流程时间):所有工序加工时间之和(∑ processingTime)</li>
* <li>SetupTime(总换型时间):若换型时间只由产品类型决定则为 0;若必须换型则为换型时间之和</li>
* <li>MachineLoad(机器负载均衡标准差):完美均衡下为 0</li>
* <li>Tardiness(总延迟时间):0(可全部按时交付)</li>
* </ul>
*
* 作者:佟礼
*/
public class KpiLowerBoundCalculator {
/**
* 对已解码的 chromosome 计算各维度的理论下界,并存入 chromosome.LowerBoundObjectives。
*
* @param chromosome 已完成 decode 的染色体
* @param globalParam 全局参数(用于确定 objectives 数组的顺序与配置)
*/
public static void computeAndSetLowerBounds(Chromosome chromosome, GlobalParam globalParam) {
double[] objectives = chromosome.getObjectives();
if (objectives == null || objectives.length == 0) return;
double[] lowerBounds = new double[objectives.length];
// 按 GlobalParam.objectiveConfigs 的顺序(与 objectives 数组一一对应)
// 注意:globalParam.objectiveConfigs 已按 level 排序
List<ObjectiveConfig> configs = globalParam.getObjectiveConfigs();
for (int i = 0; i < objectives.length && i < configs.size(); i++) {
ObjectiveConfig config = configs.get(i);
if (!config.isEnabled()) {
lowerBounds[i] = 0.0;
continue;
}
String name = config.getName();
if (GlobalParam.OBJECTIVE_MAKESPAN.equals(name)) {
lowerBounds[i] = computeMakespanLowerBound(chromosome);
} else if (GlobalParam.OBJECTIVE_TARDINESS.equals(name)) {
// 延迟理论上界为 0(全部按时)
lowerBounds[i] = 0.0;
} else if (GlobalParam.OBJECTIVE_SETUP_TIME.equals(name)) {
// 换型时间下界:若换型只由产品类型决定则为 0
lowerBounds[i] = 0.0;
} else if (GlobalParam.OBJECTIVE_FLOW_TIME.equals(name)) {
lowerBounds[i] = computeFlowTimeLowerBound(chromosome);
} else if (GlobalParam.OBJECTIVE_MACHINE_LOAD.equals(name)) {
// 负载均衡标准差理论上界为 0
lowerBounds[i] = 0.0;
} else {
lowerBounds[i] = 0.0;
}
}
chromosome.setLowerBoundObjectives(lowerBounds);
}
/**
* 计算 Makespan(最大完工时间)的理论下界 = DAG 关键路径长度。
*
* <p>思路:对 job shop 问题,完工时间下界 = 所有工序的最长无冲突调度路径长度,
* 即在不考虑机器冲突的条件下,从根节点到终点的最长加权和路径(边权重 = 加工时间)。
* 通过拓扑排序 + 动态规划实现(时间复杂度 O(V+E))。
*/
private static double computeMakespanLowerBound(Chromosome chromosome) {
List<GAScheduleResult> result = chromosome.getResult();
List<GroupResult> operatRel = chromosome.getOperatRel();
List<Entry> allOperations = chromosome.getAllOperations();
if (result == null || result.isEmpty()) return 0.0;
// ---- 1. 构建 nodeId -> processingTime 映射 ----
Map<Integer, Double> entryProcessingTime = new HashMap<>();
if (allOperations != null) {
for (Entry e : allOperations) {
entryProcessingTime.put(e.getId(), (double) e.getMinProcessingTime());
}
}
// 如果 allOperations 缺失,从 result 推算(使用 flowTime 作为加工时间近似)
if (entryProcessingTime.isEmpty()) {
for (GAScheduleResult r : result) {
entryProcessingTime.put(r.getOperationId(), Math.max(1.0, r.getProcessingTime()));
}
}
// ---- 2. 构建 DAG:nodeId -> [childNodeIds] ----
Map<Integer, List<Integer>> dag = new HashMap<>();
Set<Integer> allNodeIds = new HashSet<>();
// 初始化所有节点
for (GAScheduleResult r : result) {
int eid = r.getOperationId();
dag.putIfAbsent(eid, new ArrayList<>());
allNodeIds.add(eid);
}
// 添加边:parent -> child(基于 GroupResult.newParentIds)
if (operatRel != null) {
for (GroupResult gr : operatRel) {
List<NodeInfo> nodes = gr.getNodeInfoList();
if (nodes == null) continue;
for (NodeInfo node : nodes) {
Integer nodeId = node.getGlobalSerial();
dag.putIfAbsent(nodeId, new ArrayList<>());
allNodeIds.add(nodeId);
List<Integer> childIds = node.getNewChildIds();
if (childIds != null) {
for (Integer childId : childIds) {
dag.get(nodeId).add(childId);
allNodeIds.add(childId);
}
}
}
}
}
// ---- 3. 计算入度 ----
Map<Integer, Integer> inDegree = new HashMap<>();
for (Integer nodeId : allNodeIds) {
inDegree.put(nodeId, 0);
}
for (Integer parent : dag.keySet()) {
for (Integer child : dag.get(parent)) {
inDegree.merge(child, 1, Integer::sum);
}
}
// ---- 4. 拓扑排序 + 动态规划找最长路径 ----
// earliest[nodeId] = 从任意根节点到 nodeId 的最长路径
Map<Integer, Double> earliest = new HashMap<>();
Queue<Integer> queue = new LinkedList<>();
// 初始化:入度为 0 的节点 earliest = processingTime
for (Integer nodeId : allNodeIds) {
if (inDegree.get(nodeId) == 0) {
double pt = entryProcessingTime.getOrDefault(nodeId, 0.0);
earliest.put(nodeId, pt);
queue.offer(nodeId);
}
}
while (!queue.isEmpty()) {
Integer parent = queue.poll();
double parentEarliest = earliest.getOrDefault(parent, 0.0);
double parentPt = entryProcessingTime.getOrDefault(parent, 0.0);
double parentFinish = parentEarliest; // finish = start + pt,这里 start = parentEarliest - pt
for (Integer child : dag.getOrDefault(parent, Collections.emptyList())) {
double childPt = entryProcessingTime.getOrDefault(child, 0.0);
double childEarliestCandidate = parentEarliest + childPt;
earliest.put(child, Math.max(earliest.getOrDefault(child, 0.0), childEarliestCandidate));
inDegree.merge(child, -1, Integer::sum);
if (inDegree.get(child) == 0) {
queue.offer(child);
}
}
}
// ---- 5. 关键路径 = 所有节点 earliest 的最大值 ----
double criticalPath = 0.0;
for (double val : earliest.values()) {
if (val > criticalPath) criticalPath = val;
}
return criticalPath;
}
/**
* 计算 FlowTime(总流程时间)的理论下界 = 所有工序的加工时间之和。
*/
private static double computeFlowTimeLowerBound(Chromosome chromosome) {
List<GAScheduleResult> result = chromosome.getResult();
if (result == null || result.isEmpty()) return 0.0;
return result.stream()
.mapToDouble(r -> Math.max(1.0, r.getProcessingTime()))
.sum();
}
/**
* 计算总 Tardiness 的 Gap。
* lowerBound = 0,所以 Gap = current(即延迟小时数的绝对值)。
* 结果为 (current - 0) / 1 = current,与延迟同量纲。
*/
public static double computeTardinessGap(double tardiness) {
if (tardiness <= 0) return 0.0;
return tardiness; // lowerBound=0,用 1 做分母
}
/**
* 计算 Makespan Gap。
*/
public static double computeMakespanGap(double makespan, double lowerBound) {
if (lowerBound <= 0) return 0.0;
return (makespan - lowerBound) / lowerBound;
}
/**
* 计算 FlowTime Gap。
*/
public static double computeFlowTimeGap(double flowTime, double lowerBound) {
if (lowerBound <= 0) return 0.0;
return (flowTime - lowerBound) / lowerBound;
}
/**
* 计算 MachineLoad Gap(标准差的下界为 0)。
*/
public static double computeMachineLoadGap(double machineLoadStd) {
if (machineLoadStd <= 0) return 0.0;
return machineLoadStd; // lowerBound=0
}
/**
* 格式化 Gap 为百分比字符串。
*/
public static String formatGap(double gap) {
if (gap == 0.0) return "0.00%";
return String.format("%.2f%%", gap * 100.0);
}
/**
* 生成 KPI Gap 报告字符串(供调用方直接打印)。
* 格式:`KPI Gap 报告 (越小越好): [Makespan: cur / LB → Gap=x%] [FlowTime: ...] ...`
*/
public static String generateGapReport(Chromosome chromosome) {
double[] objectives = chromosome.getObjectives();
double[] lowerBounds = chromosome.getLowerBoundObjectives();
if (objectives == null || objectives.length == 0) {
return "KPI Gap 报告: 无 objectives 数据";
}
String[] names = {"Makespan", "FlowTime", "SetupTime", "MachineLoad", "Tardiness"};
StringBuilder sb = new StringBuilder("KPI Gap 报告 (越小越好): ");
for (int i = 0; i < objectives.length; i++) {
double current = objectives[i];
double lb = (lowerBounds != null && i < lowerBounds.length) ? lowerBounds[i] : 0.0;
double gap;
if (lb > 0) {
gap = (current - lb) / lb;
} else if (current > 0) {
gap = current; // lowerBound=0 时 gap = current 本身(绝对偏离量)
} else {
gap = 0.0;
}
String name = i < names.length ? names[i] : ("Obj[" + i + "]");
sb.append(String.format("[%s: %.4f / LB=%.4f → Gap=%s] ",
name, current, lb, formatGap(gap)));
}
return sb.toString();
}
}
...@@ -265,6 +265,7 @@ public class RoutingDataService { ...@@ -265,6 +265,7 @@ public class RoutingDataService {
List<ProdEquipment> Equipments = ProdEquipments.stream() List<ProdEquipment> Equipments = ProdEquipments.stream()
.filter(t -> t.getExecId().equals(op.getExecId())) .filter(t -> t.getExecId().equals(op.getExecId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
double minProcessingTime=999999999;
if (Equipments != null && Equipments.size() > 0) { if (Equipments != null && Equipments.size() > 0) {
List<MachineOption> mos = new ArrayList<>(); List<MachineOption> mos = new ArrayList<>();
for (ProdEquipment e : Equipments) { for (ProdEquipment e : Equipments) {
...@@ -277,7 +278,7 @@ public class RoutingDataService { ...@@ -277,7 +278,7 @@ public class RoutingDataService {
totalprocessTime=e.getSpeed()/e.getSingleOut().doubleValue()*entry.getQuantity(); totalprocessTime=e.getSpeed()/e.getSingleOut().doubleValue()*entry.getQuantity();
} }
minProcessingTime=Math.min(minProcessingTime,totalprocessTime);
if(machineIds.containsKey(e.getEquipId())) if(machineIds.containsKey(e.getEquipId()))
{ {
if( machineIds.get(e.getEquipId())<totalprocessTime) if( machineIds.get(e.getEquipId())<totalprocessTime)
...@@ -307,6 +308,7 @@ public class RoutingDataService { ...@@ -307,6 +308,7 @@ public class RoutingDataService {
mos.add(mo); mos.add(mo);
} }
entry.setMinProcessingTime(minProcessingTime);
entry.setMachineOptions(mos); entry.setMachineOptions(mos);
} }
} }
......
// Source code is decompiled from a .class file using FernFlower decompiler (from Intellij IDEA).
package com.aps.service.Algorithm; package com.aps.service.Algorithm;
import com.aps.common.util.FileHelper; import com.aps.common.util.FileHelper;
import com.aps.common.util.GlobalCacheUtil; import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.ProductionDeepCopyUtil; import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.*; import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult; import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.basic.*; import com.aps.entity.basic.Entry;
import com.aps.entity.basic.Machine;
import java.util.*; import com.aps.entity.basic.Material;
import java.util.concurrent.*; import com.aps.entity.basic.Order;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* 模拟退火算法
*/
public class SimulatedAnnealing { public class SimulatedAnnealing {
private final Random rnd = new Random(); private final Random rnd = new Random();
private static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 1.0E-4;
// ==================== 改进判断参数 ==================== private List<Entry> allOperations;
private static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 0.0001; // 显著改进阈值:只有改进超过这个值才重置无改进计数 private List<Order> orders;
private TreeMap<String, Material> materials;
private List<GroupResult> _entryRel;
private FitnessCalculator fitnessCalculator;
private Map<String, Entry> entrys;
private Map<Integer, Entry> entrybyids;
private List<Machine> cachedMachines;
private List<Order> cachedOrders;
private List<GroupResult> cachedEntryRel;
private TreeMap<String, Material> cachedMaterials;
private List<Entry> cachedAllOperations;
private final ExecutorService decodeExecutor;
private void log(String message) { private void log(String message) {
log(message, false); this.log(message, false);
} }
private void log(String message, boolean enableLogging) { private void log(String message, boolean enableLogging) {
if (enableLogging) { if (enableLogging) {
FileHelper.writeLogFile(message); FileHelper.writeLogFile(message);
} }
}
private List<Entry> allOperations;
private List<Order> orders; }
private TreeMap<String, Material> materials;
private List<GroupResult> _entryRel;
private FitnessCalculator fitnessCalculator;
private Map<String, Entry> entrys;
private Map<Integer, Entry> entrybyids;
public SimulatedAnnealing( List<Entry> allOperations, List<Order> orders,
TreeMap<String, Material> materials,List<GroupResult> entryRel, FitnessCalculator _fitnessCalculator) {
public SimulatedAnnealing(List<Entry> allOperations, List<Order> orders, TreeMap<String, Material> materials, List<GroupResult> entryRel, FitnessCalculator _fitnessCalculator) {
this.decodeExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors() - 1, Runtime.getRuntime().availableProcessors() - 1, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(200), new ThreadPoolExecutor.CallerRunsPolicy());
this.allOperations = allOperations; this.allOperations = allOperations;
this.orders = orders; this.orders = orders;
this.materials = materials; this.materials = materials;
this._entryRel = entryRel;
_entryRel=entryRel; Map<Integer, Object> mp = this.buildEntryKey();
Map<Integer, Object> mp = buildEntryKey();
this.fitnessCalculator = _fitnessCalculator; this.fitnessCalculator = _fitnessCalculator;
entrys=(Map<String, Entry>)mp.get(1); this.entrys = (Map)mp.get(1);
entrybyids=(Map<Integer, Entry>)mp.get(2); this.entrybyids = (Map)mp.get(2);
this.cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(allOperations), Entry.class);
} this.cachedOrders = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(orders), Order.class);
private final ExecutorService decodeExecutor = new ThreadPoolExecutor( this.cachedEntryRel = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(entryRel), GroupResult.class);
Runtime.getRuntime().availableProcessors() - 1, // 核心线程数=CPU-1,无切换开销 this.cachedMaterials = ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class);
Runtime.getRuntime().availableProcessors() - 1, // 最大线程数=核心数
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(200), // 有界队列,避免内存溢出
new ThreadPoolExecutor.CallerRunsPolicy() // 任务满了主线程执行,不丢失任务
);
public List<Chromosome> batchSearch(List<Chromosome> chromosomes,VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
List<Chromosome> saHcOptimized=new ArrayList<>();
// CompletableFuture.allOf(chromosomes.stream()
// .map(chromosome -> CompletableFuture.runAsync(() -> {
// Chromosome optimized = searchWithHillClimbing(chromosome, decoder, param);
// saHcOptimized.add(optimized);
// }, decodeExecutor))
// .toArray(CompletableFuture[]::new))
// .join();
for (Chromosome chromosome:chromosomes) {
Chromosome optimized = searchWithHillClimbing(chromosome,vns, decoder, machines);
saHcOptimized.add(optimized);
}
return saHcOptimized;
} }
public Chromosome batchSearchGetMax(List<Chromosome> chromosomes,VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) { public List<Chromosome> batchSearch(List<Chromosome> chromosomes, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
List<Chromosome> saHcOptimized=batchSearch(chromosomes,vns,decoder,machines); List<Chromosome> saHcOptimized = new ArrayList();
int bestidx= Getbest(saHcOptimized,null); for(Chromosome chromosome : chromosomes) {
if(bestidx>-1) { Chromosome optimized = this.searchWithHillClimbing(chromosome, vns, decoder, machines);
return saHcOptimized.get(bestidx); saHcOptimized.add(optimized);
} }
return null;
return saHcOptimized;
} }
/** public Chromosome batchSearchGetMax(List<Chromosome> chromosomes, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
* 模拟退火搜索,当温度降低到一定程度后切换到爬山法 List<Chromosome> saHcOptimized = this.batchSearch(chromosomes, vns, decoder, machines);
* 流程:模拟退火全局探索(按概率接受劣解)→ 降温 → 温度低时爬山法局部求精 → 输出最优 int bestidx = this.Getbest(saHcOptimized, (Chromosome)null);
*/ return bestidx > -1 ? (Chromosome)saHcOptimized.get(bestidx) : null;
public Chromosome searchWithHillClimbing(Chromosome chromosome, VariableNeighborhoodSearch vns,GeneticDecoder decoder, List<Machine> machines) { }
log("模拟退火+爬山法 - 开始执行",true);
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
writeKpi(best);
// 记录初始KPI用于计算改进率 public Chromosome searchWithHillClimbing(Chromosome chromosome, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
double[] initialFitnessLevel = best.getFitnessLevel().clone(); this.log("模拟退火+爬山法 - 开始执行", true);
Chromosome current = vns.copyChromosome(chromosome);
this.decode(decoder, current, machines);
Chromosome best = this.lightCopy(current);
this.writeKpi(best);
double[] initialFitnessLevel = (double[])best.getFitnessLevel().clone();
double initialFitness = best.getFitness(); double initialFitness = best.getFitness();
double temperature = (double)100.0F;
// log("模拟退火+爬山法 - 初始化解码完成"); double coolingRate = 0.9;
double temperatureThreshold = (double)5.0F;
// 初始化温度(优化:更快收敛) int maxIterations = 100;
double temperature = 100.0;
double coolingRate = 0.90; // 优化:降温更快
double temperatureThreshold = 5.0; // 优化:温度阈值更高
int maxIterations = 100; // 优化:从300减少到100次
int noImproveCount = 0; int noImproveCount = 0;
int maxNoImprove = 15; // 优化:从30减少到15次 int maxNoImprove = 15;
int stagnantWindow = 15;
// 新增:改进率监控参数 int[] recentImprovements = new int[stagnantWindow];
int stagnantWindow = 15; // 观察窗口大小 double improvementRateThreshold = 0.05;
int[] recentImprovements = new int[stagnantWindow]; // 记录最近窗口内的改进情况 this.log(String.format("模拟退火+爬山法 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d", temperature, coolingRate, temperatureThreshold, maxIterations, maxNoImprove));
double improvementRateThreshold = 0.05; // 改进率阈值(5%)
log(String.format("模拟退火+爬山法 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d",
temperature, coolingRate, temperatureThreshold, maxIterations, maxNoImprove));
int acceptCount = 0; int acceptCount = 0;
int improveCount = 0; int improveCount = 0;
int significantImproveCount = 0; int significantImproveCount = 0;
int totalIterations = 0; int totalIterations = 0;
for (int i = 0; i < maxIterations; i++) { for(int i = 0; i < maxIterations; ++i) {
totalIterations = i + 1; totalIterations = i + 1;
boolean improved = false; boolean improved = false;
decoder.DelOrder(current); decoder.DelOrder(current);
// 1. 使用智能策略生成邻域解(找瓶颈工序/设备)
Chromosome neighbor = vns.generateNeighbor(current); Chromosome neighbor = vns.generateNeighbor(current);
this.decode(decoder, neighbor, machines);
// 2. 解码 double energyDifference = this.calculateEnergyDifference(neighbor, current);
decode(decoder, neighbor,machines);
// 3. 计算能量差
double energyDifference = calculateEnergyDifference(neighbor, current);
// 4. 按概率接受新解(模拟退火核心:有概率接受劣解)
boolean accepted = false; boolean accepted = false;
if (energyDifference > 0 || rnd.nextDouble() < Math.exp(energyDifference / temperature)) { if (energyDifference > (double)0.0F || this.rnd.nextDouble() < Math.exp(energyDifference / temperature)) {
current = neighbor; current = neighbor;
accepted = true; accepted = true;
acceptCount++; ++acceptCount;
if (this.isBetter(neighbor, best)) {
// 更新全局最优 best = this.lightCopy(neighbor);
if (isBetter(current, best)) { this.writeKpi(best);
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
writeKpi(best);
improved = true; improved = true;
improveCount++; ++improveCount;
boolean isSignificant = isSignificantImprovement(current, best); boolean isSignificant = this.isSignificantImprovement(neighbor, best);
if (isSignificant) { if (isSignificant) {
noImproveCount = 0; // 只有显著改进才重置无改进计数 noImproveCount = 0;
significantImproveCount++; ++significantImproveCount;
logImprovementDetails(best, initialFitnessLevel, initialFitness, totalIterations); this.logImprovementDetails(best, initialFitnessLevel, initialFitness, totalIterations);
log(String.format("模拟退火+爬山法 - 迭代%d:找到更优解(显著),fitness=%.4f", totalIterations, best.getFitness()),true); this.log(String.format("模拟退火+爬山法 - 迭代%d:找到更优解(显著),fitness=%.4f", totalIterations, best.getFitness()), true);
} else { } else {
// 微小改进也接受,但不重置计数 this.log(String.format("模拟退火+爬山法 - 迭代%d:找到更优解(微小),fitness=%.4f", totalIterations, best.getFitness()), true);
log(String.format("模拟退火+爬山法 - 迭代%d:找到更优解(微小),fitness=%.4f", totalIterations, best.getFitness()),true);
} }
} }
} }
if (!improved) { if (!improved) {
noImproveCount++; ++noImproveCount;
} }
// 记录本次改进情况
if (totalIterations <= stagnantWindow) { if (totalIterations <= stagnantWindow) {
recentImprovements[totalIterations - 1] = improved ? 1 : 0; recentImprovements[totalIterations - 1] = improved ? 1 : 0;
} }
// 5. 降温
temperature *= coolingRate; temperature *= coolingRate;
if (totalIterations % 10 == 0) {
System.gc();
}
// 每30次迭代输出一次状态 if (totalIterations % 30 == 0) {
if ((totalIterations) % 30 == 0) { this.log(String.format("模拟退火+爬山法 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%", totalIterations, maxIterations, temperature, acceptCount, improveCount, noImproveCount, totalIterations > 0 ? (double)improveCount / (double)totalIterations * (double)100.0F : (double)0.0F));
log(String.format("模拟退火+爬山法 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%",
totalIterations, maxIterations, temperature, acceptCount, improveCount, noImproveCount,
totalIterations > 0 ? (double)improveCount / totalIterations * 100 : 0));
} }
// 6. 提前停止条件
boolean shouldStop = false; boolean shouldStop = false;
String stopReason = ""; String stopReason = "";
if (temperature < temperatureThreshold) { if (temperature < temperatureThreshold) {
shouldStop = true; shouldStop = true;
stopReason = "温度低于阈值"; stopReason = "温度低于阈值";
...@@ -201,194 +164,141 @@ public class SimulatedAnnealing { ...@@ -201,194 +164,141 @@ public class SimulatedAnnealing {
shouldStop = true; shouldStop = true;
stopReason = String.format("连续无改进达到上限(%d次)", maxNoImprove); stopReason = String.format("连续无改进达到上限(%d次)", maxNoImprove);
} else if (totalIterations >= stagnantWindow) { } else if (totalIterations >= stagnantWindow) {
// 检查改进率是否过低 double recentImproveRate = this.calculateRecentImprovementRate(recentImprovements, stagnantWindow);
double recentImproveRate = calculateRecentImprovementRate(recentImprovements, stagnantWindow);
if (recentImproveRate < improvementRateThreshold) { if (recentImproveRate < improvementRateThreshold) {
shouldStop = true; shouldStop = true;
stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * 100); stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * (double)100.0F);
} }
} }
if (shouldStop) { if (shouldStop) {
log(String.format("模拟退火+爬山法 - 提前停止:%s,迭代%d次,最终温度=%.4f", this.log(String.format("模拟退火+爬山法 - 提前停止:%s,迭代%d次,最终温度=%.4f", stopReason, totalIterations, temperature));
stopReason, totalIterations, temperature)); this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations); this.log("模拟退火+爬山法 - 切换到爬山法求精");
HillClimbing hillClimbing = new HillClimbing(this.allOperations, this.orders, this.materials, this._entryRel, this.fitnessCalculator);
log("模拟退火+爬山法 - 切换到爬山法求精");
HillClimbing hillClimbing = new HillClimbing( allOperations,orders,materials,_entryRel,fitnessCalculator);
Chromosome refined = hillClimbing.search(best, decoder, machines); Chromosome refined = hillClimbing.search(best, decoder, machines);
log("模拟退火+爬山法 - 爬山法求精完成"); this.log("模拟退火+爬山法 - 爬山法求精完成");
return refined; return refined;
} }
} }
log(String.format("模拟退火+爬山法 - 完成所有%d次迭代,最终fitness=%.4f", this.log(String.format("模拟退火+爬山法 - 完成所有%d次迭代,最终fitness=%.4f", maxIterations, best.getFitness()), true);
maxIterations, best.getFitness()),true); this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
// 7. 输出全局最优排产
return best; return best;
} }
/** public Chromosome search(Chromosome chromosome, TabuSearch tabusearch, VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
* 模拟退火搜索 this.log("模拟退火 - 开始执行", true);
* 流程:模拟退火全局探索(按概率接受劣解)→ 降温 → 输出最优
*/
public Chromosome search(Chromosome chromosome,TabuSearch tabusearch,VariableNeighborhoodSearch vns, GeneticDecoder decoder, List<Machine> machines) {
log("模拟退火 - 开始执行",true);
Chromosome current = vns.copyChromosome(chromosome); Chromosome current = vns.copyChromosome(chromosome);
this.decode(decoder, current, machines);
decode(decoder, current,machines); Chromosome best = this.lightCopy(current);
this.writeKpi(best);
double[] initialFitnessLevel = (double[])best.getFitnessLevel().clone();
Chromosome best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
writeKpi(best);
// 初始化解码
// 记录初始KPI用于计算改进率
double[] initialFitnessLevel = best.getFitnessLevel().clone();
double initialFitness = best.getFitness(); double initialFitness = best.getFitness();
double temperature = (double)100.0F;
// log("模拟退火+爬山法 - 初始化解码完成"); double coolingRate = 0.9;
double temperatureThreshold = (double)5.0F;
// 初始化温度(优化:更快收敛) int maxIterations = 80;
double temperature = 100.0;
double coolingRate = 0.90; // 优化:降温更快
double temperatureThreshold = 5.0; // 优化:温度阈值更高
int maxIterations = 80; // 优化:从300减少到80次
int noImproveCount = 0; int noImproveCount = 0;
int maxNoImprove = 10; // 优化:从20减少到10次 int maxNoImprove = 10;
int stagnantWindow = 10;
// 新增:改进率监控参数 int[] recentImprovements = new int[stagnantWindow];
int stagnantWindow = 10; // 观察窗口大小 double improvementRateThreshold = 0.001;
int[] recentImprovements = new int[stagnantWindow]; // 记录最近窗口内的改进情况 this.log(String.format("模拟退火 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d", temperature, coolingRate, temperatureThreshold, maxIterations, maxNoImprove));
double improvementRateThreshold = 0.001; // 改进率阈值
log(String.format("模拟退火 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d",
temperature, coolingRate, temperatureThreshold, maxIterations, maxNoImprove));
int acceptCount = 0; int acceptCount = 0;
int improveCount = 0; int improveCount = 0;
int significantImproveCount = 0; int significantImproveCount = 0;
int totalIterations = 0; int totalIterations = 0;
for (int i = 0; i < maxIterations; i++) { for(int i = 0; i < maxIterations; ++i) {
totalIterations = i + 1; totalIterations = i + 1;
boolean improved = false; boolean improved = false;
log(String.format("模拟退火 - 迭代%d:", totalIterations)); this.log(String.format("模拟退火 - 迭代%d:", totalIterations));
decoder.DelOrder(current); decoder.DelOrder(current);
// 1. 使用智能策略生成邻域解(找瓶颈工序/设备)
Chromosome neighbor = vns.generateNeighbor(current); Chromosome neighbor = vns.generateNeighbor(current);
// 2. 解码 this.decode(decoder, neighbor, machines);
decode(decoder, neighbor,machines); if (tabusearch.isTabu(neighbor.getGeneStr()) && !this.isBetter(current, best)) {
// 跳过禁忌解(除非是最优解)
if (tabusearch.isTabu(neighbor.getGeneStr()) && !isBetter(current,best)) {
temperature *= coolingRate; temperature *= coolingRate;
noImproveCount++; ++noImproveCount;
// 记录本次无改进
if (totalIterations <= stagnantWindow) { if (totalIterations <= stagnantWindow) {
recentImprovements[totalIterations - 1] = 0; recentImprovements[totalIterations - 1] = 0;
} }
continue; } else {
} double energyDifference = this.calculateEnergyDifference(neighbor, current);
boolean accepted = false;
// 3. 计算能量差 if (energyDifference > (double)0.0F || this.rnd.nextDouble() < Math.exp(energyDifference / temperature)) {
double energyDifference = calculateEnergyDifference(neighbor, current); current = neighbor;
++acceptCount;
// 4. 按概率接受新解(模拟退火核心:有概率接受劣解) if (this.isBetter(neighbor, best)) {
boolean accepted = false; best = this.lightCopy(neighbor);
if (energyDifference > 0 || rnd.nextDouble() < Math.exp(energyDifference / temperature)) { tabusearch.addToTabuList(best.getGeneStr());
current = neighbor; this.writeKpi(best);
improved = true;
acceptCount++; ++improveCount;
if (isBetter(current, best)) { boolean isSignificant = this.isSignificantImprovement(neighbor, best);
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class); if (isSignificant) {
tabusearch.addToTabuList(best.getGeneStr()); noImproveCount = 0;
writeKpi(best); ++significantImproveCount;
improved = true; this.logImprovementDetails(best, initialFitnessLevel, initialFitness, totalIterations);
improveCount++; this.log(String.format("模拟退火 - 迭代%d:找到更优解(显著),fitness=%.4f", totalIterations, best.getFitness()));
boolean isSignificant = isSignificantImprovement(current, best); } else {
if (isSignificant) { this.log(String.format("模拟退火 - 迭代%d:找到更优解(微小),fitness=%.4f", totalIterations, best.getFitness()));
noImproveCount = 0; }
significantImproveCount++;
logImprovementDetails(best, initialFitnessLevel, initialFitness, totalIterations);
log(String.format("模拟退火 - 迭代%d:找到更优解(显著),fitness=%.4f", totalIterations, best.getFitness()));
} else {
log(String.format("模拟退火 - 迭代%d:找到更优解(微小),fitness=%.4f", totalIterations, best.getFitness()));
} }
} }
// decoder.DelOrder(current);
}
if (!improved) { if (!improved) {
noImproveCount++; ++noImproveCount;
} }
// 记录本次改进情况
if (totalIterations <= stagnantWindow) {
recentImprovements[totalIterations - 1] = improved ? 1 : 0;
}
// 5. 降温
temperature *= coolingRate;
// 每次迭代都输出状态 if (totalIterations <= stagnantWindow) {
log(String.format("模拟退火 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%", recentImprovements[totalIterations - 1] = improved ? 1 : 0;
totalIterations, maxIterations, temperature, acceptCount, improveCount, noImproveCount, }
totalIterations > 0 ? (double)improveCount / totalIterations * 100 : 0));
// 6. 检查提前停止条件 temperature *= coolingRate;
boolean shouldStop = false; if (totalIterations % 10 == 0) {
String stopReason = ""; System.gc();
}
if (temperature < temperatureThreshold) { this.log(String.format("模拟退火 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%", totalIterations, maxIterations, temperature, acceptCount, improveCount, noImproveCount, totalIterations > 0 ? (double)improveCount / (double)totalIterations * (double)100.0F : (double)0.0F));
shouldStop = true; boolean shouldStop = false;
stopReason = "温度低于阈值"; String stopReason = "";
} else if (noImproveCount >= maxNoImprove) { if (temperature < temperatureThreshold) {
shouldStop = true;
stopReason = String.format("连续无改进达到上限(%d次)", maxNoImprove);
} else if (totalIterations >= stagnantWindow) {
// 检查改进率是否过低
double recentImproveRate = calculateRecentImprovementRate(recentImprovements, stagnantWindow);
if (recentImproveRate < improvementRateThreshold) {
shouldStop = true; shouldStop = true;
stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * 100); stopReason = "温度低于阈值";
} else if (noImproveCount >= maxNoImprove) {
shouldStop = true;
stopReason = String.format("连续无改进达到上限(%d次)", maxNoImprove);
} else if (totalIterations >= stagnantWindow) {
double recentImproveRate = this.calculateRecentImprovementRate(recentImprovements, stagnantWindow);
if (recentImproveRate < improvementRateThreshold) {
shouldStop = true;
stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * (double)100.0F);
}
} }
}
if (shouldStop) { if (shouldStop) {
log(String.format("模拟退火 - 提前停止:%s,迭代%d次,最终温度=%.4f", this.log(String.format("模拟退火 - 提前停止:%s,迭代%d次,最终温度=%.4f", stopReason, totalIterations, temperature));
stopReason, totalIterations, temperature)); this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations); return best;
return best; }
} }
} }
log(String.format("模拟退火 - 完成所有%d次迭代,最终fitness=%.4f", this.log(String.format("模拟退火 - 完成所有%d次迭代,最终fitness=%.4f", maxIterations, best.getFitness()), true);
maxIterations, best.getFitness()),true); this.logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
logFinalSummary(best, initialFitnessLevel, initialFitness, improveCount, significantImproveCount, totalIterations);
// 7. 输出全局最优排产
return best; return best;
} }
/**
* 记录改进详情
*/
private void logImprovementDetails(Chromosome best, double[] initialFitnessLevel, double initialFitness, int iteration) { private void logImprovementDetails(Chromosome best, double[] initialFitnessLevel, double initialFitness, int iteration) {
StringBuilder sb = new StringBuilder("模拟退火 - 改进详情: 迭代" + iteration + ", "); StringBuilder sb = new StringBuilder("模拟退火 - 改进详情: 迭代" + iteration + ", ");
double[] currentFitness = best.getFitnessLevel(); double[] currentFitness = best.getFitnessLevel();
if (currentFitness != null && currentFitness.length > 0 && initialFitnessLevel != null && initialFitnessLevel.length > 0) {
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length); int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
for(int i = 0; i < minLength; ++i) {
double improvement = currentFitness[i] - initialFitnessLevel[i]; double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(+%.4f) ", i+1, initialFitnessLevel[i], currentFitness[i], improvement)); sb.append(String.format("KPI%d: %.4f→%.4f(+%.4f) ", i + 1, initialFitnessLevel[i], currentFitness[i], improvement));
} }
} else { } else {
sb.append("(KPI数据不可用) "); sb.append("(KPI数据不可用) ");
...@@ -396,219 +306,191 @@ public class SimulatedAnnealing { ...@@ -396,219 +306,191 @@ public class SimulatedAnnealing {
double totalImprovement = best.getFitness() - initialFitness; double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(+%.4f)", initialFitness, best.getFitness(), totalImprovement)); sb.append(String.format("总Fitness: %.4f→%.4f(+%.4f)", initialFitness, best.getFitness(), totalImprovement));
log(sb.toString()); this.log(sb.toString());
} }
/**
* 计算最近改进率
*/
private double calculateRecentImprovementRate(int[] recentImprovements, int windowSize) { private double calculateRecentImprovementRate(int[] recentImprovements, int windowSize) {
int improveCount = 0; int improveCount = 0;
for (int i = 0; i < windowSize; i++) {
for(int i = 0; i < windowSize; ++i) {
improveCount += recentImprovements[i]; improveCount += recentImprovements[i];
} }
return (double) improveCount / windowSize;
return (double)improveCount / (double)windowSize;
} }
/**
* 记录最终总结
*/
private void logFinalSummary(Chromosome best, double[] initialFitnessLevel, double initialFitness, int improveCount, int significantImproveCount, int totalIterations) { private void logFinalSummary(Chromosome best, double[] initialFitnessLevel, double initialFitness, int improveCount, int significantImproveCount, int totalIterations) {
StringBuilder sb = new StringBuilder("模拟退火 - 最终总结: "); StringBuilder sb = new StringBuilder("模拟退火 - 最终总结: ");
double[] currentFitness = best.getFitnessLevel(); double[] currentFitness = best.getFitnessLevel();
sb.append(String.format("总迭代%d次, 成功改进%d次(显著%d次), 改进率%.2f%%. ", totalIterations, improveCount, significantImproveCount, totalIterations > 0 ? (double)improveCount / (double)totalIterations * (double)100.0F : (double)0.0F));
sb.append(String.format("总迭代%d次, 成功改进%d次(显著%d次), 改进率%.2f%%. ", if (currentFitness != null && currentFitness.length > 0 && initialFitnessLevel != null && initialFitnessLevel.length > 0) {
totalIterations, improveCount, significantImproveCount,
totalIterations > 0 ? (double)improveCount / totalIterations * 100 : 0));
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length); int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
for(int i = 0; i < minLength; ++i) {
double improvement = currentFitness[i] - initialFitnessLevel[i]; double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(%.2f%%) ", i+1, initialFitnessLevel[i], currentFitness[i], sb.append(String.format("KPI%d: %.4f→%.4f(%.2f%%) ", i + 1, initialFitnessLevel[i], currentFitness[i], initialFitnessLevel[i] > (double)0.0F ? improvement / initialFitnessLevel[i] * (double)100.0F : (double)0.0F));
initialFitnessLevel[i] > 0 ? improvement / initialFitnessLevel[i] * 100 : 0));
} }
} else { } else {
sb.append("(KPI数据不可用) "); sb.append("(KPI数据不可用) ");
} }
double totalImprovement = best.getFitness() - initialFitness; double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(%.2f%%)", initialFitness, best.getFitness(), sb.append(String.format("总Fitness: %.4f→%.4f(%.2f%%)", initialFitness, best.getFitness(), initialFitness > (double)0.0F ? totalImprovement / initialFitness * (double)100.0F : (double)0.0F));
initialFitness > 0 ? totalImprovement / initialFitness * 100 : 0)); this.log(sb.toString());
log(sb.toString());
} }
private void writeKpi(Chromosome chromosome) {
private void writeKpi(Chromosome chromosome) {
String fitness = ""; String fitness = "";
double[] fitness1 = chromosome.getFitnessLevel(); double[] fitness1 = chromosome.getFitnessLevel();
if (fitness1 != null) { if (fitness1 != null) {
for (int i = 0; i < fitness1.length; i++) { for(int i = 0; i < fitness1.length; ++i) {
fitness += fitness1[i] + ","; fitness = fitness + fitness1[i] + ",";
} }
} else { } else {
fitness = "null (未计算)"; fitness = "null (未计算)";
} }
log(String.format("模拟退火 - kpi:%s", fitness),true); this.log(String.format("模拟退火 - kpi:%s", fitness), true);
if(chromosome.getMakespan()!=0) { if (chromosome.getMakespan() != (double)0.0F) {
FileHelper.writeLogFile(String.format("模拟退火 - kpi-Makespan: %f", chromosome.getMakespan())); FileHelper.writeLogFile(String.format("模拟退火 - kpi-Makespan: %f", chromosome.getMakespan()));
} }
if(chromosome.getDelayTime()!=0) {
FileHelper.writeLogFile(String.format("模拟退火 - kpi-DelayTime: %f", chromosome.getDelayTime())); if (chromosome.getDelayTime() != (double)0.0F) {
FileHelper.writeLogFile(String.format("模拟退火 - kpi-DelayTime: %f", chromosome.getDelayTime()));
} }
if(chromosome.getTotalChangeoverTime()!=0) {
if (chromosome.getTotalChangeoverTime() != (double)0.0F) {
FileHelper.writeLogFile(String.format("模拟退火 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime())); FileHelper.writeLogFile(String.format("模拟退火 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime()));
} }
if(chromosome.getMachineLoadStd()!=0) {
if (chromosome.getMachineLoadStd() != (double)0.0F) {
FileHelper.writeLogFile(String.format("模拟退火 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd())); FileHelper.writeLogFile(String.format("模拟退火 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd()));
} }
if(chromosome.getTotalFlowTime()!=0) {
if (chromosome.getTotalFlowTime() != (double)0.0F) {
FileHelper.writeLogFile(String.format("模拟退火 - kpi-FlowTime: %f", chromosome.getTotalFlowTime())); FileHelper.writeLogFile(String.format("模拟退火 - kpi-FlowTime: %f", chromosome.getTotalFlowTime()));
} }
} }
/**
* 计算能量差(基于fitnessLevel数组的比较)
*/
private double calculateEnergyDifference(Chromosome neighbor, Chromosome current) { private double calculateEnergyDifference(Chromosome neighbor, Chromosome current) {
double[] neighborFitness = neighbor.getFitnessLevel(); double[] neighborFitness = neighbor.getFitnessLevel();
double[] currentFitness = current.getFitnessLevel(); double[] currentFitness = current.getFitnessLevel();
double diff = (double)0.0F;
// 计算加权能量差 for(int i = 0; i < neighborFitness.length; ++i) {
double diff = 0; diff += neighborFitness[i] - currentFitness[i];
for (int i = 0; i < neighborFitness.length; i++) {
diff += (neighborFitness[i] - currentFitness[i]);
} }
return diff; return diff;
} }
/**
* 按优先级分组工序
*/
private Map<Double, List<Entry>> groupOperationsByPriority() { private Map<Double, List<Entry>> groupOperationsByPriority() {
Map<Double, List<Entry>> groups = new HashMap<>(); Map<Double, List<Entry>> groups = new HashMap();
for (Entry op : allOperations) {
for(Entry op : this.allOperations) {
double priority = op.getPriority(); double priority = op.getPriority();
groups.computeIfAbsent(priority, k -> new ArrayList<>()).add(op); ((List)groups.computeIfAbsent(priority, (k) -> new ArrayList())).add(op);
} }
// 过滤掉:设备只有一个且只有一个GroupId的优先级组 Map<Double, List<Entry>> filteredGroups = new HashMap();
Map<Double, List<Entry>> filteredGroups = new HashMap<>();
for (Map.Entry<Double, List<Entry>> entry : groups.entrySet()) {
List<Entry> ops = entry.getValue();
// 检查是否所有工序都只有一个设备选项
boolean allSingleMachine = ops.stream()
.allMatch(op -> op.getMachineOptions().size() <= 1);
// 检查是否只有一个GroupId for(Map.Entry<Double, List<Entry>> entry : groups.entrySet()) {
Set<Integer> groupIds = ops.stream() List<Entry> ops = (List)entry.getValue();
.map(Entry::getGroupId) boolean allSingleMachine = ops.stream().allMatch((opx) -> opx.getMachineOptions().size() <= 1);
.collect(Collectors.toSet()); Set<Integer> groupIds = (Set)ops.stream().map(Entry::getGroupId).collect(Collectors.toSet());
if (!allSingleMachine || groupIds.size() > 1) {
// 如果两个条件都满足,过滤掉这个优先级组 filteredGroups.put((Double)entry.getKey(), ops);
if (!(allSingleMachine && groupIds.size() <= 1)) {
filteredGroups.put(entry.getKey(), ops);
} }
} }
return filteredGroups; return filteredGroups;
} }
/**
* 构建Entry索引:op.getGroupId() + "_" + op.getSequence() -> Entry
*/
private Map<Integer, Object> buildEntryKey() { private Map<Integer, Object> buildEntryKey() {
Map<Integer, Object> index0 = new HashMap<>(); Map<Integer, Object> index0 = new HashMap();
Map<String, Entry> index = new HashMap<>(); Map<String, Entry> index = new HashMap();
Map<Integer, Entry> index2 = new HashMap<>(); Map<Integer, Entry> index2 = new HashMap();
List<Entry> allOps = this.allOperations;
for (Entry op : allOps) { for(Entry op : this.allOperations) {
String key = op.getGroupId() + "_" + op.getSequence(); String key = op.getGroupId() + "_" + op.getSequence();
index.put(key, op); index.put(key, op);
index2.put(op.getId(), op); index2.put(op.getId(), op);
} }
index0.put(1,index);
index0.put(2,index2); index0.put(1, index);
index0.put(2, index2);
return index0; return index0;
} }
/** private void decode(GeneticDecoder decoder, Chromosome chromosome, List<Machine> machines) {
* 解码染色体 chromosome.setResult(new CopyOnWriteArrayList());
*/ if (this.cachedMachines == null) {
/** this.cachedMachines = ProductionDeepCopyUtil.deepCopyList(machines, Machine.class);
* 解码染色体 }
*/
private void decode(GeneticDecoder decoder, Chromosome chromosome , List<Machine> machines) { chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(this.cachedMachines, Machine.class));
chromosome.setResult(new CopyOnWriteArrayList<>()); chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(this.cachedOrders), Order.class));
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(this.cachedEntryRel), GroupResult.class));
// 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射 chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(this.cachedMaterials, String.class, Material.class));
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList(this.cachedAllOperations), Entry.class));
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝 List<GAScheduleResult> lockedOrders = (List)GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class) ); // 简单拷贝,实际可能需要深拷贝
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(_entryRel), GroupResult.class) ); // 简单拷贝,实际可能需要深拷贝
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(materials,String.class, Material.class)); // 简单拷贝,实际可能需要深拷贝
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class) ); // 简单拷贝,实际可能需要深拷贝
//chromosome.setObjectiveWeights(_objectiveWeights);
// chromosome.setBaseTime(param.getBaseTime());
// chromosome.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝
// _sceneService.saveChromosomeToFile(chromosome, "12345679");
// 加载锁定工单到ResultOld
List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
if (lockedOrders != null && !lockedOrders.isEmpty()) { if (lockedOrders != null && !lockedOrders.isEmpty()) {
chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class)); chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
log("将 " + lockedOrders.size() + " 个锁定工单加载到初始种群中"); this.log("将 " + lockedOrders.size() + " 个锁定工单加载到初始种群中");
} else { } else {
chromosome.setResultOld(new CopyOnWriteArrayList<>()); chromosome.setResultOld(new CopyOnWriteArrayList());
} }
decoder.decodeChromosomeWithCache(chromosome,false);
decoder.decodeChromosomeWithCache(chromosome, false);
} }
private Chromosome lightCopy(Chromosome source) {
Chromosome copy = new Chromosome();
copy.setOperationSequencing(new CopyOnWriteArrayList(source.getOperationSequencing()));
copy.setMachineSelection(new CopyOnWriteArrayList(source.getMachineSelection()));
copy.setGlobalOpList(new CopyOnWriteArrayList(source.getGlobalOpList()));
copy.setOrders(new CopyOnWriteArrayList(source.getOrders()));
copy.setAllOperations(new CopyOnWriteArrayList(source.getAllOperations()));
copy.setResult(source.getResult());
copy.setMachines(source.getMachines());
copy.setOperatRel(new CopyOnWriteArrayList(source.getOperatRel()));
copy.setScenarioID(source.getScenarioID());
copy.setBaseTime(source.getBaseTime());
copy.setGenerateType(source.getGenerateType());
copy.setFitnessLevel(source.getFitnessLevel());
copy.setFitness(source.getFitness());
return copy;
}
/**
* 比较两个染色体的优劣(基于fitnessLevel多层次比较)
*/
private boolean isBetter(Chromosome c1, Chromosome c2) { private boolean isBetter(Chromosome c1, Chromosome c2) {
return fitnessCalculator.isBetter(c1,c2); return this.fitnessCalculator.isBetter(c1, c2);
} }
/**
* 判断是否为显著改进(只有超过阈值的改进才重置无改进计数)
*/
private boolean isSignificantImprovement(Chromosome newChromo, Chromosome oldChromo) { private boolean isSignificantImprovement(Chromosome newChromo, Chromosome oldChromo) {
if (!isBetter(newChromo, oldChromo)) { if (!this.isBetter(newChromo, oldChromo)) {
return false; return false;
} else {
double newFitness = newChromo.getFitness();
double oldFitness = oldChromo.getFitness();
return newFitness - oldFitness > 1.0E-4;
} }
double newFitness = newChromo.getFitness();
double oldFitness = oldChromo.getFitness();
return (newFitness - oldFitness) > SIGNIFICANT_IMPROVEMENT_THRESHOLD;
} }
private int Getbest(List<Chromosome> candidates,Chromosome best) {
// 找出最佳候选方案
private int Getbest(List<Chromosome> candidates, Chromosome best) {
int bestidx = -1; int bestidx = -1;
if(best==null) if (best == null) {
{ best = (Chromosome)candidates.get(0);
best=candidates.get(0); bestidx = 0;
bestidx=0;
} }
for (int i = 0; i < candidates.size(); i++) {
Chromosome candidate = candidates.get(i); for(int i = 0; i < candidates.size(); ++i) {
if (isBetter(candidate, best)) { Chromosome candidate = (Chromosome)candidates.get(i);
if (this.isBetter(candidate, best)) {
bestidx = i; bestidx = i;
} }
} }
return bestidx; return bestidx;
} }
}
}
\ No newline at end of file
...@@ -71,32 +71,8 @@ public class VariableNeighborhoodSearch { ...@@ -71,32 +71,8 @@ public class VariableNeighborhoodSearch {
private static final double DIVERSITY_WEIGHT = 0.4; // 设备选择多样性权重(越高越倾向选择次数少的设备) private static final double DIVERSITY_WEIGHT = 0.4; // 设备选择多样性权重(越高越倾向选择次数少的设备)
private static final double RANDOM_NOISE_FOR_MACHINE = 0.1; // 机器选择的随机扰动因子 private static final double RANDOM_NOISE_FOR_MACHINE = 0.1; // 机器选择的随机扰动因子
// 日志级别
private static final int LOG_LEVEL_DEBUG = 0;
private static final int LOG_LEVEL_INFO = 1;
private static final int LOG_LEVEL_WARN = 2;
private int currentLogLevel = LOG_LEVEL_INFO;
// 局部搜索优化
private static final int MAX_LOCAL_SEARCH_NEIGHBORS = 2; // 从5减少到2,大幅减少解码次数 private static final int MAX_LOCAL_SEARCH_NEIGHBORS = 2; // 从5减少到2,大幅减少解码次数
private void log(String message) {
log(message, LOG_LEVEL_INFO, false);
}
private void log(String message, boolean enableLogging) {
log(message, LOG_LEVEL_INFO, enableLogging);
}
private void log(String message, int level) {
log(message, level, false);
}
private void log(String message, int level, boolean enableLogging) {
if (enableLogging && level >= currentLogLevel) {
FileHelper.writeLogFile(message);
}
}
private List<Entry> allOperations; private List<Entry> allOperations;
...@@ -140,6 +116,23 @@ public class VariableNeighborhoodSearch { ...@@ -140,6 +116,23 @@ public class VariableNeighborhoodSearch {
private TreeMap<String, Material> cachedMaterials; private TreeMap<String, Material> cachedMaterials;
private List<Entry> cachedAllOperations; private List<Entry> cachedAllOperations;
// CP-SAT 邻域需要的缓存
private GeneticDecoder cpSatDecoder;
private List<Machine> cpSatMachines;
private ObjectiveWeights cpSatWeights;
private boolean cpSatEnabled = false;
/**
* 启用 CP-SAT 邻域
*/
public void enableCpSatNeighborhood(GeneticDecoder decoder, List<Machine> machines, ObjectiveWeights weights) {
this.cpSatDecoder = decoder;
this.cpSatMachines = machines;
this.cpSatWeights = weights;
this.cpSatEnabled = true;
FileHelper.writeLogFile("[VNS-CpSat] CP-SAT 邻域已启用");
}
private GeneticOperations geneticOperations; private GeneticOperations geneticOperations;
// 邻域结构成功率统计(用于 search() 方法) // 邻域结构成功率统计(用于 search() 方法)
...@@ -261,26 +254,41 @@ public class VariableNeighborhoodSearch { ...@@ -261,26 +254,41 @@ public class VariableNeighborhoodSearch {
// HybridShake - 混合抖动:综合调整 // HybridShake - 混合抖动:综合调整
neighborhoods.add(new NeighborhoodStructure("HybridShake", this::hybridShakeWrapper)); neighborhoods.add(new NeighborhoodStructure("HybridShake", this::hybridShakeWrapper));
// CpSatLocalOptimize - CP-SAT 局部重优化(仅当启用时)
neighborhoods.add(new NeighborhoodStructure("CpSatLocalOptimize", this::cpSatLocalOptimizeWrapper));
return neighborhoods; return neighborhoods;
} }
/** /**
* 定义邻域结构(按成功率排序) * 定义邻域结构(按成功率排序,CP-SAT 始终保留一个名额
*/ */
private List<NeighborhoodStructure> defineNeighborhoods() { private List<NeighborhoodStructure> defineNeighborhoods() {
// 按成功率排序 // 按成功率排序
List<NeighborhoodWithStats> sorted = new ArrayList<>(neighborhoodsWithStats); List<NeighborhoodWithStats> sorted = new ArrayList<>(neighborhoodsWithStats);
sorted.sort((a, b) -> Double.compare(b.getSuccessRate(), a.getSuccessRate())); sorted.sort((a, b) -> Double.compare(b.getSuccessRate(), a.getSuccessRate()));
// 提取 NeighborhoodStructure
List<NeighborhoodStructure> result = new ArrayList<>(); List<NeighborhoodStructure> result = new ArrayList<>();
// for (NeighborhoodWithStats ns : sorted) {
// result.add(ns.structure);
// }
// 始终保留 CP-SAT 邻域(避免因成功率低被挤出前 N 名后永远无法被选中)
NeighborhoodWithStats cpSatNs = null;
int maxNeighborhoods = Math.min(MAX_NEIGHBORHOODS, sorted.size()); int maxNeighborhoods = Math.min(MAX_NEIGHBORHOODS, sorted.size());
for (int i = 0; i < maxNeighborhoods; i++) { for (int i = 0; i < maxNeighborhoods; i++) {
result.add(sorted.get(i).structure); NeighborhoodWithStats ns = sorted.get(i);
if ("CpSatLocalOptimize".equals(ns.structure.name)) {
cpSatNs = ns;
}
result.add(ns.structure);
}
// 如果 CP-SAT 未被前 N 名选中且已启用,额外追加
if (cpSatNs == null && cpSatEnabled) {
for (NeighborhoodWithStats ns : neighborhoodsWithStats) {
if ("CpSatLocalOptimize".equals(ns.structure.name)) {
result.add(ns.structure);
break;
}
}
} }
return result; return result;
...@@ -304,7 +312,7 @@ public class VariableNeighborhoodSearch { ...@@ -304,7 +312,7 @@ public class VariableNeighborhoodSearch {
/** /**
* 对种群中的每个个体进行变邻域搜索 * 对种群中的每个个体进行变邻域搜索
*/ */
public List<Chromosome> search(List<Chromosome> population,TabuSearch tabuSearch, GeneticDecoder decoder, List<Machine> machines) { public List<Chromosome> search(List<Chromosome> population, TabuSearch tabuSearch, GeneticDecoder decoder, List<Machine> machines) {
List<Chromosome> improvedPopulation = new ArrayList<>(); List<Chromosome> improvedPopulation = new ArrayList<>();
for (Chromosome chromosome : population) { for (Chromosome chromosome : population) {
...@@ -339,8 +347,9 @@ public class VariableNeighborhoodSearch { ...@@ -339,8 +347,9 @@ public class VariableNeighborhoodSearch {
log("变邻域搜索(共用禁忌表) - 开始执行",true); log("变邻域搜索(共用禁忌表) - 开始执行",true);
// 深拷贝当前染色体 // 深拷贝当前染色体
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class); Chromosome current = lightCopy(chromosome);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class); Chromosome best = copyChromosome(chromosome);
writeKpi(best); writeKpi(best);
// 记录初始KPI用于跟踪改进 // 记录初始KPI用于跟踪改进
...@@ -421,9 +430,9 @@ public class VariableNeighborhoodSearch { ...@@ -421,9 +430,9 @@ public class VariableNeighborhoodSearch {
} }
if (accept) { if (accept) {
current = ProductionDeepCopyUtil.deepCopy(localBest, Chromosome.class); current = lightCopy(localBest);
if (betterThanBest) { if (betterThanBest) {
best = ProductionDeepCopyUtil.deepCopy(localBest, Chromosome.class); best = lightCopy(localBest);
writeKpi(best); writeKpi(best);
totalImprovements++; totalImprovements++;
roundHadImprovement = true; roundHadImprovement = true;
...@@ -535,6 +544,8 @@ public class VariableNeighborhoodSearch { ...@@ -535,6 +544,8 @@ public class VariableNeighborhoodSearch {
logVNSFinalSummary(best, initialFitnessLevel, initialFitness, totalRounds, totalImprovements, totalSignificantImprovements); logVNSFinalSummary(best, initialFitnessLevel, initialFitness, totalRounds, totalImprovements, totalSignificantImprovements);
log(String.format("变邻域搜索(融合禁忌) - 结束, 总轮次=%d", totalRounds), true); log(String.format("变邻域搜索(融合禁忌) - 结束, 总轮次=%d", totalRounds), true);
decode(decoder, best, machines);
return best; return best;
} }
...@@ -616,21 +627,25 @@ public class VariableNeighborhoodSearch { ...@@ -616,21 +627,25 @@ public class VariableNeighborhoodSearch {
log(String.format("变邻域搜索 - kpi:%s", fitness),true); log(String.format("变邻域搜索 - kpi:%s", fitness),true);
if(chromosome.getMakespan()!=0) { if(chromosome.getMakespan()!=0) {
FileHelper.writeLogFile(String.format("变邻域搜索 - kpi-Makespan: %f", chromosome.getMakespan())); log(String.format("变邻域搜索 - kpi-Makespan: %f", chromosome.getMakespan()));
} }
if(chromosome.getDelayTime()!=0) { if(chromosome.getDelayTime()!=0) {
FileHelper.writeLogFile(String.format("变邻域搜索 - kpi-DelayTime: %f", chromosome.getDelayTime())); log(String.format("变邻域搜索 - kpi-DelayTime: %f", chromosome.getDelayTime()));
} }
if(chromosome.getTotalChangeoverTime()!=0) { if(chromosome.getTotalChangeoverTime()!=0) {
FileHelper.writeLogFile(String.format("变邻域搜索 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime())); log(String.format("变邻域搜索 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime()));
} }
if(chromosome.getMachineLoadStd()!=0) { if(chromosome.getMachineLoadStd()!=0) {
FileHelper.writeLogFile(String.format("变邻域搜索 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd())); log(String.format("变邻域搜索 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd()));
} }
if(chromosome.getTotalFlowTime()!=0) { if(chromosome.getTotalFlowTime()!=0) {
FileHelper.writeLogFile(String.format("变邻域搜索 - kpi-FlowTime: %f",chromosome.getTotalFlowTime())); log(String.format("变邻域搜索 - kpi-FlowTime: %f",chromosome.getTotalFlowTime()));
} }
// ==================== 打印各 KPI 的 Gap ====================
log(KpiLowerBoundCalculator.generateGapReport(chromosome));
} }
/** /**
...@@ -2125,12 +2140,23 @@ public class VariableNeighborhoodSearch { ...@@ -2125,12 +2140,23 @@ public class VariableNeighborhoodSearch {
Chromosome neighbor=new Chromosome(); Chromosome neighbor=new Chromosome();
neighbor.setGenerateType(chromosome.getGenerateType()); neighbor.setGenerateType(chromosome.getGenerateType());
neighbor.setID(UUID.randomUUID().toString()); neighbor.setID(UUID.randomUUID().toString());
neighbor.setOperationSequencing(chromosome.getOperationSequencing()); neighbor.setOperationSequencing(new CopyOnWriteArrayList<>(chromosome.getOperationSequencing()));
neighbor.setMachineSelection(chromosome.getMachineSelection()); neighbor.setMachineSelection(new CopyOnWriteArrayList<>(chromosome.getMachineSelection()));
neighbor.setGlobalOpList(new CopyOnWriteArrayList<>(chromosome.getGlobalOpList()));
neighbor.setScenarioID(chromosome.getScenarioID()); neighbor.setScenarioID(chromosome.getScenarioID());
neighbor.setBaseTime(chromosome.getBaseTime()); neighbor.setBaseTime(chromosome.getBaseTime());
neighbor.setFitnessLevel(chromosome.getFitnessLevel()); neighbor.setFitnessLevel(chromosome.getFitnessLevel());
neighbor.setGlobalOpList(chromosome.getGlobalOpList()); // 拷贝 orders/allOperations/operatRel,确保 DelOrder 能正常清理 SF 工序
if (chromosome.getOrders() != null) {
neighbor.setOrders(new CopyOnWriteArrayList<>(chromosome.getOrders()));
}
if (chromosome.getAllOperations() != null) {
neighbor.setAllOperations(new CopyOnWriteArrayList<>(chromosome.getAllOperations()));
}
if (chromosome.getOperatRel() != null) {
neighbor.setOperatRel(new CopyOnWriteArrayList<>(chromosome.getOperatRel()));
}
return neighbor; return neighbor;
} }
...@@ -2380,25 +2406,36 @@ public class VariableNeighborhoodSearch { ...@@ -2380,25 +2406,36 @@ public class VariableNeighborhoodSearch {
CopyOnWriteArrayList<Integer> os = neighbor.getOperationSequencing(); CopyOnWriteArrayList<Integer> os = neighbor.getOperationSequencing();
CopyOnWriteArrayList<Integer> ms = neighbor.getMachineSelection(); CopyOnWriteArrayList<Integer> ms = neighbor.getMachineSelection();
log(String.format("generateSameMachineSwapNeighbor: os.size=%d, ms.size=%d, idx1=%d, globalOpList.size=%d",
os.size(), ms.size(), idx1,
neighbor.getGlobalOpList() != null ? neighbor.getGlobalOpList().size() : 0));
if (os.size() < 2 || ms.size() < 2) { if (os.size() < 2 || ms.size() < 2) {
log("generateSameMachineSwapNeighbor: os或ms太小,返回");
return neighbor; return neighbor;
} }
// ========== 修复1: op1 必须非 null ========== // ========== 修复1: op1 必须非 null ==========
Entry op1 = positionIndex.get(idx1); Entry op1 = positionIndex.get(idx1);
if (op1 == null) { if (op1 == null) {
log(String.format("generateSameMachineSwapNeighbor: positionIndex中找不到idx1=%d", idx1));
return neighbor; return neighbor;
} }
log(String.format("generateSameMachineSwapNeighbor: op1=订单%d工序%d, machineOptions.size=%d",
op1.getGroupId(), op1.getSequence(), op1.getMachineOptions().size()));
// ========== 修复2: machinePositionIndex 查找必须非 null 且有效 ========== // ========== 修复2: machinePositionIndex 查找必须非 null 且有效 ==========
String op1Key = op1.getGroupId() + "_" + op1.getSequence(); String op1Key = op1.getGroupId() + "_" + op1.getSequence();
Integer maPos1 = machinePositionIndex.get(op1Key); Integer maPos1 = machinePositionIndex.get(op1Key);
if (maPos1 == null || maPos1 < 0 || maPos1 >= ms.size()) { if (maPos1 == null || maPos1 < 0 || maPos1 >= ms.size()) {
log(String.format("generateSameMachineSwapNeighbor: maPos1=%s无效 (ms.size=%d)", maPos1, ms.size()));
return neighbor; return neighbor;
} }
int machineSeq1 = ms.get(maPos1); int machineSeq1 = ms.get(maPos1);
if (machineSeq1 < 1 || machineSeq1 > op1.getMachineOptions().size()) { if (machineSeq1 < 1 || machineSeq1 > op1.getMachineOptions().size()) {
log(String.format("generateSameMachineSwapNeighbor: machineSeq1=%d超出范围[1-%d]",
machineSeq1, op1.getMachineOptions().size()));
return neighbor; return neighbor;
} }
...@@ -2444,6 +2481,11 @@ public class VariableNeighborhoodSearch { ...@@ -2444,6 +2481,11 @@ public class VariableNeighborhoodSearch {
Collections.swap(os, idx1, idx2); Collections.swap(os, idx1, idx2);
neighbor.setOperationSequencing(os); neighbor.setOperationSequencing(os);
log(String.format("generateSameMachineSwapNeighbor: swap完成 idx1=%d(订单%d工序%d) <-> idx2=%d(订单%d工序%d), 同机器候选数=%d",
idx1, op1.getGroupId(), op1.getSequence(),
idx2, positionIndex.get(idx2).getGroupId(), positionIndex.get(idx2).getSequence(),
sameMachineOsPositions.size()));
return neighbor; return neighbor;
} }
...@@ -2512,8 +2554,8 @@ public class VariableNeighborhoodSearch { ...@@ -2512,8 +2554,8 @@ public class VariableNeighborhoodSearch {
Chromosome best =copyChromosome(chromosome); Chromosome best =copyChromosome(chromosome);
decode(decoder, best, machines); decode(decoder, best, machines);
Chromosome current = ProductionDeepCopyUtil.deepCopy(best, Chromosome.class); Chromosome current = lightCopy(best);
geneticOperations.DelOrder(current);
writeKpi(best); writeKpi(best);
// 预定义邻域结构,避免每次循环重复创建 // 预定义邻域结构,避免每次循环重复创建
List<NeighborhoodStructure> neighborhoods = defineNeighborhoods(); List<NeighborhoodStructure> neighborhoods = defineNeighborhoods();
...@@ -2558,6 +2600,29 @@ public class VariableNeighborhoodSearch { ...@@ -2558,6 +2600,29 @@ public class VariableNeighborhoodSearch {
* 解码染色体 * 解码染色体
*/ */
private void decode(GeneticDecoder decoder, Chromosome chromosome , List<Machine> machines) { private void decode(GeneticDecoder decoder, Chromosome chromosome , List<Machine> machines) {
// MS 校验:解码前检查 machineSelection 与 machineOptions 是否匹配
List<GlobalOperationInfo> gops = chromosome.getGlobalOpList();
List<Integer> msCheck = chromosome.getMachineSelection();
if (gops != null && msCheck != null) {
int msErrors = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Math.min(gops.size(), msCheck.size()); i++) {
Entry op = gops.get(i).getOp();
int msVal = msCheck.get(i);
if (op != null && op.getMachineOptions() != null
&& (msVal < 1 || msVal > op.getMachineOptions().size())) {
msErrors++;
if (msErrors <= 3) {
sb.append(String.format(" [idx=%d 订单%d工序%d ms=%d range=1-%d]",
i, op.getGroupId(), op.getSequence(), msVal, op.getMachineOptions().size()));
}
}
}
if (msErrors > 0) {
log(String.format("decode-MS校验失败: 共%d处越界 %s", msErrors, sb.toString()));
}
}
chromosome.setResult(new CopyOnWriteArrayList<>()); chromosome.setResult(new CopyOnWriteArrayList<>());
// 缓存 Machine 列表(第一次调用时缓存) // 缓存 Machine 列表(第一次调用时缓存)
...@@ -2724,7 +2789,28 @@ public class VariableNeighborhoodSearch { ...@@ -2724,7 +2789,28 @@ public class VariableNeighborhoodSearch {
} }
return index; return index;
} }
/**
* 轻量拷贝:只复制 generateNeighbor/DelOrder 需要的字段,避免全量 JSON 深拷贝导致 OOM。
* result/machines/operatRel 等重型数据共享引用(generateNeighbor 只读,不修改)。
*/
private Chromosome lightCopy(Chromosome source) {
Chromosome copy = new Chromosome();
copy.setOperationSequencing(new CopyOnWriteArrayList<>(source.getOperationSequencing()));
copy.setMachineSelection(new CopyOnWriteArrayList<>(source.getMachineSelection()));
copy.setGlobalOpList(new CopyOnWriteArrayList<>(source.getGlobalOpList()));
copy.setOrders(new CopyOnWriteArrayList<>(source.getOrders()));
copy.setAllOperations(new CopyOnWriteArrayList<>(source.getAllOperations()));
copy.setResult(source.getResult());
copy.setMachines(source.getMachines());
copy.setOperatRel(new CopyOnWriteArrayList<>(source.getOperatRel()));
copy.setScenarioID(source.getScenarioID());
copy.setBaseTime(source.getBaseTime());
copy.setGenerateType(source.getGenerateType());
copy.setFitnessLevel(source.getFitnessLevel());
copy.setFitness(source.getFitness());
geneticOperations.DelOrder(copy);
return copy;
}
/** /**
* 构建位置->Entry索引 * 构建位置->Entry索引
*/ */
...@@ -3675,10 +3761,10 @@ public class VariableNeighborhoodSearch { ...@@ -3675,10 +3761,10 @@ public class VariableNeighborhoodSearch {
// 随机选择一个不同于当前的机器 // 随机选择一个不同于当前的机器
int newSelection; int newSelection;
if (options.size() == 2) { if (options.size() == 2) {
newSelection = (currentSelection == 1) ? 0 : 1; newSelection = (currentSelection == 1) ? 2 : 1;
} else { } else {
do { do {
newSelection = rnd.nextInt(options.size()); newSelection = rnd.nextInt(options.size()) + 1;
} while (newSelection == currentSelection); } while (newSelection == currentSelection);
} }
...@@ -3736,4 +3822,43 @@ public class VariableNeighborhoodSearch { ...@@ -3736,4 +3822,43 @@ public class VariableNeighborhoodSearch {
log("HybridShake: 混合抖动完成"); log("HybridShake: 混合抖动完成");
return neighbor; return neighbor;
} }
/**
* CP-SAT 局部重优化邻域
* 在当前调度的基础上,释放一部分工序,用 CP-SAT 重优化机器选择
*/
private Chromosome cpSatLocalOptimizeWrapper(Chromosome chromosome) {
if (!cpSatEnabled || cpSatDecoder == null || cpSatMachines == null) {
return null;
}
try {
// CP-SAT 需要解码后的时间信息,先做一次解码
cpSatDecoder.serialDecode(chromosome);
// 只做一次快速重优化(释放约 10% 的工序)
CpSatLnsNeighborhood lns = new CpSatLnsNeighborhood(
cachedAllOperations, cpSatMachines,orders,materials,_entryRel, fitnessCalculator);
int releaseCount = Math.max(30, (int)(cachedAllOperations.size() * 0.10));
int timeLimitSec = Math.min(8, 12000 / Math.max(100, cachedAllOperations.size()));
Chromosome neighbor = lns.optimizeNeighborhood(chromosome, releaseCount, timeLimitSec);
if (neighbor != null) {
log("CpSatLocalOptimize: 生成邻居成功");
}
return neighbor;
} catch (Exception e) {
log("CpSatLocalOptimize: 异常 - " + e.getMessage());
return null;
}
}
public static void log(String message) {
log(message, true);
}
public static void log(String message, boolean enableLogging) {
FileHelper.log(message, enableLogging);
}
} }
\ No newline at end of file
...@@ -43,8 +43,8 @@ public class PlanResultServiceTest { ...@@ -43,8 +43,8 @@ public class PlanResultServiceTest {
// planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000 // planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000
// planResultService.execute2("E1448B3C9C8743DEAB39708F2CFE348A");//倒排bomces // planResultService.execute2("E1448B3C9C8743DEAB39708F2CFE348A");//倒排bomces
// planResultService.execute2("197083D0D26A449EB179AC103C753FD3"); planResultService.execute2("85DA28EC5F4643449E65A51253D5F127");
planResultService.execute2("F8F147BD627C47B1A190399DD7A697F6"); // planResultService.execute2("F8F147BD627C47B1A190399DD7A697F6");
// planResultService.execute2("9FEDFD92BB6A4675BF9B1CC64505D1AB"); // planResultService.execute2("9FEDFD92BB6A4675BF9B1CC64505D1AB");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment