mirror of
https://github.com/L-yang-yang/cugenopt.git
synced 2026-04-24 12:06:22 +02:00
329 lines
11 KiB
Text
329 lines
11 KiB
Text
|
|
#include "solver.cuh"
|
|||
|
|
#include "multi_gpu_solver.cuh"
|
|||
|
|
#include "bi_objective_vrp.cuh"
|
|||
|
|
#include "tri_objective_vrp.cuh"
|
|||
|
|
#include "bi_objective_knapsack.cuh"
|
|||
|
|
#include <cstdio>
|
|||
|
|
#include <cstdlib>
|
|||
|
|
#include <cmath>
|
|||
|
|
#include <vector>
|
|||
|
|
#include <fstream>
|
|||
|
|
#include <sstream>
|
|||
|
|
#include <string>
|
|||
|
|
|
|||
|
|
// 确保使用 std:: 命名空间的数学函数
|
|||
|
|
using std::sqrt;
|
|||
|
|
using std::round;
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 数据加载工具
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
// 加载 A-n32-k5 VRP 实例(EUC_2D 格式)
|
|||
|
|
struct VRPInstance {
|
|||
|
|
float* dist;
|
|||
|
|
float* demand;
|
|||
|
|
int n;
|
|||
|
|
float capacity;
|
|||
|
|
int optimal_vehicles;
|
|||
|
|
float optimal_distance;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
VRPInstance load_an32k5() {
|
|||
|
|
// A-n32-k5 坐标(包含 depot)
|
|||
|
|
const float coords[32][2] = {
|
|||
|
|
{82,76},
|
|||
|
|
{96,44},{50,5},{49,8},{13,7},{29,89},{58,30},{84,39},{14,24},{2,39},
|
|||
|
|
{3,82},{5,10},{98,52},{84,25},{61,59},{1,65},{88,51},{91,2},{19,32},
|
|||
|
|
{93,3},{50,93},{98,14},{5,42},{42,9},{61,62},{9,97},{80,55},{57,69},
|
|||
|
|
{23,15},{20,70},{85,60},{98,5}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const float demands[31] = {
|
|||
|
|
19,21,6,19,7,12,16,6,16,8,14,21,16,3,22,18,19,1,24,8,12,4,8,24,24,2,20,15,2,14,9
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
VRPInstance inst;
|
|||
|
|
inst.n = 31;
|
|||
|
|
inst.capacity = 100.0f;
|
|||
|
|
inst.optimal_vehicles = 5;
|
|||
|
|
inst.optimal_distance = 784.0f;
|
|||
|
|
|
|||
|
|
// 计算 EUC_2D 距离矩阵
|
|||
|
|
inst.dist = new float[32 * 32];
|
|||
|
|
for (int i = 0; i < 32; i++) {
|
|||
|
|
for (int j = 0; j < 32; j++) {
|
|||
|
|
float dx = coords[i][0] - coords[j][0];
|
|||
|
|
float dy = coords[i][1] - coords[j][1];
|
|||
|
|
inst.dist[i * 32 + j] = std::round(std::sqrt(dx * dx + dy * dy));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
inst.demand = new float[31];
|
|||
|
|
for (int i = 0; i < 31; i++) {
|
|||
|
|
inst.demand[i] = demands[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return inst;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载 knapPI_1_100 实例
|
|||
|
|
struct KnapsackInstance {
|
|||
|
|
int* values;
|
|||
|
|
int* weights;
|
|||
|
|
int n;
|
|||
|
|
int capacity;
|
|||
|
|
int optimal_value;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
KnapsackInstance load_knapsack_100() {
|
|||
|
|
const char* filename = "../../data/knapsack/knapPI_1_100.txt";
|
|||
|
|
|
|||
|
|
std::ifstream file(filename);
|
|||
|
|
if (!file.is_open()) {
|
|||
|
|
fprintf(stderr, "Error: Cannot open %s\n", filename);
|
|||
|
|
exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int n, capacity;
|
|||
|
|
file >> n >> capacity;
|
|||
|
|
|
|||
|
|
KnapsackInstance inst;
|
|||
|
|
inst.n = n;
|
|||
|
|
inst.capacity = capacity;
|
|||
|
|
inst.optimal_value = 9147; // 已知最优值
|
|||
|
|
|
|||
|
|
inst.values = new int[n];
|
|||
|
|
inst.weights = new int[n];
|
|||
|
|
|
|||
|
|
for (int i = 0; i < n; i++) {
|
|||
|
|
file >> inst.values[i] >> inst.weights[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
file.close();
|
|||
|
|
return inst;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 实验配置
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
struct ExperimentConfig {
|
|||
|
|
const char* name;
|
|||
|
|
CompareMode mode;
|
|||
|
|
float obj_weights[MAX_OBJ];
|
|||
|
|
int obj_priority[MAX_OBJ];
|
|||
|
|
float obj_tolerance[MAX_OBJ];
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Weighted 模式配置
|
|||
|
|
ExperimentConfig WEIGHTED_CONFIGS[] = {
|
|||
|
|
{"W_90_10", CompareMode::Weighted, {0.9f, 0.1f}, {0, 1}, {0.0f, 0.0f}},
|
|||
|
|
{"W_70_30", CompareMode::Weighted, {0.7f, 0.3f}, {0, 1}, {0.0f, 0.0f}},
|
|||
|
|
{"W_50_50", CompareMode::Weighted, {0.5f, 0.5f}, {0, 1}, {0.0f, 0.0f}},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Lexicographic 模式配置(双目标)
|
|||
|
|
ExperimentConfig LEX_CONFIGS_BI[] = {
|
|||
|
|
{"L_dist_veh_t100", CompareMode::Lexicographic, {1.0f, 1.0f}, {0, 1}, {100.0f, 0.0f}},
|
|||
|
|
{"L_dist_veh_t50", CompareMode::Lexicographic, {1.0f, 1.0f}, {0, 1}, {50.0f, 0.0f}},
|
|||
|
|
{"L_veh_dist_t0", CompareMode::Lexicographic, {1.0f, 1.0f}, {1, 0}, {0.0f, 100.0f}},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Lexicographic 模式配置(三目标)
|
|||
|
|
ExperimentConfig LEX_CONFIGS_TRI[] = {
|
|||
|
|
{"L_dist_veh_max", CompareMode::Lexicographic, {1.0f, 1.0f, 1.0f}, {0, 1, 2}, {100.0f, 0.0f, 50.0f}},
|
|||
|
|
{"L_veh_dist_max", CompareMode::Lexicographic, {1.0f, 1.0f, 1.0f}, {1, 0, 2}, {0.0f, 100.0f, 50.0f}},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 实验运行函数
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
template<typename Problem>
|
|||
|
|
void run_experiment(const char* problem_name, Problem& prob,
|
|||
|
|
const ExperimentConfig& exp_cfg,
|
|||
|
|
int num_objectives,
|
|||
|
|
bool multi_gpu = false) {
|
|||
|
|
printf(" [run_experiment] 开始\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
// 应用实验配置到 Problem(通过覆盖字段)
|
|||
|
|
prob.override_mode = exp_cfg.mode;
|
|||
|
|
for (int i = 0; i < num_objectives; i++) {
|
|||
|
|
prob.override_weights[i] = exp_cfg.obj_weights[i];
|
|||
|
|
prob.override_priority[i] = exp_cfg.obj_priority[i];
|
|||
|
|
prob.override_tolerance[i] = exp_cfg.obj_tolerance[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
printf(" [run_experiment] 配置覆盖完成\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
SolverConfig cfg;
|
|||
|
|
cfg.pop_size = 64; // 固定小规模
|
|||
|
|
cfg.max_gen = 1000; // 固定代数
|
|||
|
|
cfg.time_limit_sec = 0.0f; // 不使用时间限制
|
|||
|
|
cfg.verbose = true; // 启用详细输出
|
|||
|
|
cfg.sa_temp_init = 50.0f;
|
|||
|
|
cfg.sa_alpha = 0.999f;
|
|||
|
|
cfg.num_islands = 2; // 固定岛屿数
|
|||
|
|
cfg.migrate_interval = 50;
|
|||
|
|
cfg.crossover_rate = 0.1f;
|
|||
|
|
cfg.use_aos = true; // 启用 AOS(测试延迟归一化)
|
|||
|
|
cfg.aos_update_interval = 5; // 每 5 个 batch 更新一次
|
|||
|
|
cfg.use_cuda_graph = false; // 禁用 CUDA Graph
|
|||
|
|
|
|||
|
|
printf(" [run_experiment] SolverConfig 创建完成\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
const int num_runs = 1; // 先只运行 1 次测试
|
|||
|
|
const unsigned seeds[] = {42, 123, 456, 789, 2024};
|
|||
|
|
|
|||
|
|
printf("\n[%s] %s (mode=%s, multi_gpu=%s)\n",
|
|||
|
|
problem_name, exp_cfg.name,
|
|||
|
|
exp_cfg.mode == CompareMode::Weighted ? "Weighted" : "Lexicographic",
|
|||
|
|
multi_gpu ? "YES" : "NO");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
for (int run = 0; run < num_runs; run++) {
|
|||
|
|
printf(" [run_experiment] 开始 Run %d\n", run + 1);
|
|||
|
|
fflush(stdout);
|
|||
|
|
cfg.seed = seeds[run];
|
|||
|
|
|
|||
|
|
SolveResult<typename Problem::Sol> result;
|
|||
|
|
if (multi_gpu) {
|
|||
|
|
cfg.num_gpus = 2;
|
|||
|
|
result = solve_multi_gpu(prob, cfg);
|
|||
|
|
} else {
|
|||
|
|
result = solve(prob, cfg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
printf(" Run %d (seed=%u): ", run + 1, seeds[run]);
|
|||
|
|
for (int i = 0; i < num_objectives; i++) {
|
|||
|
|
printf("obj%d=%.2f ", i, result.best_solution.objectives[i]);
|
|||
|
|
}
|
|||
|
|
printf("penalty=%.2f time=%.1fs gen=%d\n",
|
|||
|
|
result.best_solution.penalty,
|
|||
|
|
result.elapsed_ms / 1000.0f,
|
|||
|
|
result.generations);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 主函数
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
int main() {
|
|||
|
|
printf("==============================================\n");
|
|||
|
|
printf("E13: 多目标优化验证实验\n");
|
|||
|
|
printf("==============================================\n\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
// 检测 GPU
|
|||
|
|
int num_gpus;
|
|||
|
|
cudaGetDeviceCount(&num_gpus);
|
|||
|
|
cudaDeviceProp prop;
|
|||
|
|
cudaGetDeviceProperties(&prop, 0);
|
|||
|
|
printf("GPU: %s (检测到 %d 个)\n\n", prop.name, num_gpus);
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
// ========== 实验 1: 双目标 VRP (A-n32-k5) ==========
|
|||
|
|
printf("========================================\n");
|
|||
|
|
printf("实验 1: 双目标 VRP (A-n32-k5)\n");
|
|||
|
|
printf("目标: 最小化距离 + 最小化车辆数\n");
|
|||
|
|
printf("========================================\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
printf("加载数据...\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
VRPInstance vrp_inst = load_an32k5();
|
|||
|
|
printf("数据加载完成\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
// Weighted 模式测试
|
|||
|
|
printf("\n--- Weighted 模式 ---\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
printf("创建第一个 Problem...\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
auto prob = BiObjectiveVRP::create(vrp_inst.dist, vrp_inst.demand,
|
|||
|
|
vrp_inst.n, vrp_inst.capacity, 10);
|
|||
|
|
printf("Problem 创建成功,开始实验...\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
run_experiment("BiVRP", prob, WEIGHTED_CONFIGS[0], 2, false);
|
|||
|
|
|
|||
|
|
printf("第一个实验完成\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
prob.destroy();
|
|||
|
|
|
|||
|
|
// Lexicographic 模式测试
|
|||
|
|
printf("\n--- Lexicographic 模式 ---\n");
|
|||
|
|
for (int i = 0; i < 3; i++) {
|
|||
|
|
auto prob = BiObjectiveVRP::create(vrp_inst.dist, vrp_inst.demand,
|
|||
|
|
vrp_inst.n, vrp_inst.capacity, 10);
|
|||
|
|
run_experiment("BiVRP", prob, LEX_CONFIGS_BI[i], 2, false);
|
|||
|
|
prob.destroy();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 多 GPU 验证(附加)
|
|||
|
|
if (num_gpus >= 2) {
|
|||
|
|
printf("\n--- 多 GPU 附加验证 (2×GPU) ---\n");
|
|||
|
|
|
|||
|
|
// Weighted 验证
|
|||
|
|
auto prob_w = BiObjectiveVRP::create(vrp_inst.dist, vrp_inst.demand,
|
|||
|
|
vrp_inst.n, vrp_inst.capacity, 10);
|
|||
|
|
run_experiment("BiVRP_MultiGPU", prob_w, WEIGHTED_CONFIGS[1], 2, true);
|
|||
|
|
prob_w.destroy();
|
|||
|
|
|
|||
|
|
// Lexicographic 验证
|
|||
|
|
auto prob_l = BiObjectiveVRP::create(vrp_inst.dist, vrp_inst.demand,
|
|||
|
|
vrp_inst.n, vrp_inst.capacity, 10);
|
|||
|
|
run_experiment("BiVRP_MultiGPU", prob_l, LEX_CONFIGS_BI[0], 2, true);
|
|||
|
|
prob_l.destroy();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delete[] vrp_inst.dist;
|
|||
|
|
delete[] vrp_inst.demand;
|
|||
|
|
|
|||
|
|
// ========== 实验 2: 三目标 VRP (A-n32-k5) ==========
|
|||
|
|
printf("\n========================================\n");
|
|||
|
|
printf("实验 2: 三目标 VRP (A-n32-k5)\n");
|
|||
|
|
printf("目标: 最小化距离 + 最小化车辆数 + 最小化最大路径长度\n");
|
|||
|
|
printf("========================================\n");
|
|||
|
|
|
|||
|
|
vrp_inst = load_an32k5();
|
|||
|
|
|
|||
|
|
// Weighted 模式
|
|||
|
|
printf("\n--- Weighted 模式 ---\n");
|
|||
|
|
ExperimentConfig tri_weighted = {"W_60_20_20", CompareMode::Weighted, {0.6f, 0.2f, 0.2f}, {0, 1, 2}, {0.0f, 0.0f, 0.0f}};
|
|||
|
|
auto prob_tri_w = TriObjectiveVRP::create(vrp_inst.dist, vrp_inst.demand,
|
|||
|
|
vrp_inst.n, vrp_inst.capacity, 10);
|
|||
|
|
run_experiment("TriVRP", prob_tri_w, tri_weighted, 3, false);
|
|||
|
|
prob_tri_w.destroy();
|
|||
|
|
|
|||
|
|
// Lexicographic 模式
|
|||
|
|
printf("\n--- Lexicographic 模式 ---\n");
|
|||
|
|
for (int i = 0; i < 2; i++) {
|
|||
|
|
auto prob_tri_l = TriObjectiveVRP::create(vrp_inst.dist, vrp_inst.demand,
|
|||
|
|
vrp_inst.n, vrp_inst.capacity, 10);
|
|||
|
|
run_experiment("TriVRP", prob_tri_l, LEX_CONFIGS_TRI[i], 3, false);
|
|||
|
|
prob_tri_l.destroy();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delete[] vrp_inst.dist;
|
|||
|
|
delete[] vrp_inst.demand;
|
|||
|
|
|
|||
|
|
// ========== 实验 3: 双目标 Knapsack - 暂时跳过(文件读取问题) ==========
|
|||
|
|
printf("\n========================================\n");
|
|||
|
|
printf("实验 3: 双目标 Knapsack - 跳过\n");
|
|||
|
|
printf("========================================\n");
|
|||
|
|
fflush(stdout);
|
|||
|
|
|
|||
|
|
printf("\n==============================================\n");
|
|||
|
|
printf("E13 实验完成\n");
|
|||
|
|
printf("==============================================\n");
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
}
|