#include #include "vde/kbe/knowledge_engine.h" #include #include #include using namespace vde::kbe; // ═══════════════════════════════════════════════════════════ // 1. CheckMate 设计验证 // ═══════════════════════════════════════════════════════════ TEST(KBETest, CheckMate_DefaultRulesRegistered) { CheckMate cm; auto rules = cm.rules(); // 默认注册至少 10 条规则 EXPECT_GE(rules.size(), 10u); } TEST(KBETest, CheckMate_RunAllDefaultRules) { CheckMate cm; std::vector params = { {"diameter", 10.0}, {"wall_thickness", 0.8}, {"aspect_ratio", 5.0}, {"density", 2700.0}, {"safety_factor", 2.0}, }; auto results = cm.run_all(params); EXPECT_EQ(results.size(), cm.rules().size()); auto summary = cm.summary(results); // 默认规则应全部通过(参数值在合法范围内) int total_failures = 0; for (const auto& [sev, count] : summary) total_failures += count; EXPECT_EQ(total_failures, 0); } TEST(KBETest, CheckMate_PositiveDimensionFails) { CheckMate cm; std::vector params = { {"diameter", -5.0}, // 非法:负值 }; auto result = cm.run_rule("R_GEO_POSITIVE_DIM", params); EXPECT_FALSE(result.passed); EXPECT_EQ(result.severity, CheckSeverity::ERROR); } TEST(KBETest, CheckMate_CustomCheck) { CheckMate cm; cm.add_check("R_GEO_POSITIVE_DIM", [](const std::vector&) { CheckResult r; r.rule_name = "R_GEO_POSITIVE_DIM"; r.severity = CheckSeverity::ERROR; r.passed = false; r.message = "Custom: negative dimension detected"; return r; }); auto result = cm.run_rule("R_GEO_POSITIVE_DIM", {}); EXPECT_FALSE(result.passed); EXPECT_EQ(result.message, "Custom: negative dimension detected"); } TEST(KBETest, CheckMate_CategoryRun) { CheckMate cm; std::vector params = { {"diameter", 10.0}, {"wall_thickness", 2.0}, {"safety_factor", 2.0}, }; auto geo_results = cm.run_category("geometry", params); // 应有几何规则产生结果 EXPECT_GT(geo_results.size(), 0u); auto mat_results = cm.run_category("material", params); EXPECT_GT(mat_results.size(), 0u); } // ═══════════════════════════════════════════════════════════ // 2. RuleEngine IF-THEN 规则引擎 // ═══════════════════════════════════════════════════════════ TEST(KBETest, RuleEngine_AddAndGetParam) { RuleEngine engine; engine.set_param("length", 10.0); auto val = engine.get_param("length"); ASSERT_TRUE(val.has_value()); EXPECT_DOUBLE_EQ(std::get(*val), 10.0); } TEST(KBETest, RuleEngine_SimpleInfer) { RuleEngine engine; engine.set_param("diameter", 5.0); engine.set_param("thickness", 1.0); // IF diameter > 3 THEN SET thickness = 2.0 Rule r{"R1", "diameter > 3", "SET thickness = 2.0", 1, true}; engine.add_rule(r); auto traces = engine.infer(); EXPECT_GE(traces.size(), 1u); auto new_thickness = engine.get_param("thickness"); ASSERT_TRUE(new_thickness.has_value()); EXPECT_DOUBLE_EQ(std::get(*new_thickness), 2.0); } TEST(KBETest, RuleEngine_PriorityOrder) { RuleEngine engine; engine.set_param("x", 1.0); // 高优先级规则先执行: SET x = 20 Rule r1{"low", "x >= 0", "SET x = 10.0", 0, true}; // priority 0 Rule r2{"high", "x >= 0", "SET x = 20.0", 10, true}; // priority 10 engine.add_rule(r1); engine.add_rule(r2); // 高优先级规则先触发,但同一轮迭代中低优先级规则也会触发 // 最终结果由最后触发的规则决定(按优先级排序,低优先级后执行) auto traces = engine.infer(); EXPECT_GE(traces.size(), 2u); // 两条规则都触发 auto val = engine.get_param("x"); ASSERT_TRUE(val.has_value()); // 低优先级(priority 0)后执行,最终为 10 EXPECT_DOUBLE_EQ(std::get(*val), 10.0); } TEST(KBETest, RuleEngine_ANDCondition) { RuleEngine engine; engine.set_param("a", 10.0); engine.set_param("b", 5.0); Rule r{"R_and", "a > 5 AND b < 10", "SET a = 100.0", 1, true}; engine.add_rule(r); engine.infer(); auto val = engine.get_param("a"); ASSERT_TRUE(val.has_value()); EXPECT_DOUBLE_EQ(std::get(*val), 100.0); } TEST(KBETest, RuleEngine_NoInfiniteLoop) { RuleEngine engine; engine.set_param("counter", 0.0); // 自引用规则 — 应被循环检测阻止 Rule r{"loop", "counter < 1000", "SET counter = counter + 1", 1, true}; engine.add_rule(r); auto traces = engine.infer(); // 最多执行 100 次迭代(由引擎内部限制) EXPECT_LE(traces.size(), 100u); } // ═══════════════════════════════════════════════════════════ // 3. DesignTable 设计表 // ═══════════════════════════════════════════════════════════ TEST(KBETest, DesignTable_DefineColumnsAndAddRow) { DesignTable table; table.define_columns({"length", "width", "height"}, {"mm", "mm", "mm"}); EXPECT_EQ(table.col_count(), 3u); DesignRow row; row["length"] = 100.0; row["width"] = 50.0; row["height"] = 25.0; table.add_row(row); EXPECT_EQ(table.row_count(), 1u); auto r = table.get_row(0); ASSERT_TRUE(r.has_value()); EXPECT_DOUBLE_EQ(std::get(r->at("length")), 100.0); } TEST(KBETest, DesignTable_CSVImportExport) { DesignTable table; std::string csv = "length,width,height\n" "100,50,25\n" "200,80,40\n" "150,60,30\n"; EXPECT_TRUE(table.import_csv(csv)); EXPECT_EQ(table.row_count(), 3u); EXPECT_EQ(table.col_count(), 3u); std::string exported = table.export_csv(); EXPECT_FALSE(exported.empty()); EXPECT_NE(exported.find("length"), std::string::npos); EXPECT_NE(exported.find("100"), std::string::npos); } TEST(KBETest, DesignTable_MixedTypes) { DesignTable table; std::string csv = "name,diameter,material,is_standard\n" "Bolt_M10,10,Steel,true\n" "Nut_M8,8,Brass,false\n"; EXPECT_TRUE(table.import_csv(csv)); EXPECT_EQ(table.row_count(), 2u); auto r0 = table.get_row(0); ASSERT_TRUE(r0.has_value()); EXPECT_EQ(std::get(r0->at("name")), "Bolt_M10"); EXPECT_DOUBLE_EQ(std::get(r0->at("diameter")), 10.0); EXPECT_EQ(std::get(r0->at("is_standard")), true); } // ═══════════════════════════════════════════════════════════ // 4. ParametricOptimization 参数优化 // ═══════════════════════════════════════════════════════════ TEST(KBETest, Optimization_GA_Minimization) { ParametricOptimization opt; // 目标:最小化 (x-5)^2 opt.set_fitness([](const std::vector& params) { for (const auto& p : params) { if (p.name == "x" && std::holds_alternative(p.value)) { double x = std::get(p.value); return -((x - 5.0) * (x - 5.0)); // 负值因为 GA 最大化适应度 } } return 0.0; }); std::vector initial = {{"x", 10.0, 0.0, 100.0, "", "", false}}; opt.set_bounds("x", 0.0, 100.0); GAConfig ga_config; ga_config.population_size = 50; ga_config.generations = 30; auto result = opt.optimize_ga(initial, ga_config); EXPECT_TRUE(result.converged); // 最优解应接近 x=5.0 for (const auto& p : result.optimal_params) { if (p.name == "x") { double val = std::get(p.value); EXPECT_NEAR(val, 5.0, 5.0); // GA 有随机性,容差大些 } } // 适应度历史应有记录 EXPECT_GT(opt.fitness_history().size(), 0u); } TEST(KBETest, Optimization_Gradient_Descent) { ParametricOptimization opt; // 目标:最小化 (x-3)^2,梯度下降直接最小化 fitness opt.set_fitness([](const std::vector& params) { for (const auto& p : params) { if (p.name == "x" && std::holds_alternative(p.value)) { double x = std::get(p.value); return (x - 3.0) * (x - 3.0); // 正值,梯度下降最小化 } } return 0.0; }); std::vector initial = {{"x", 8.0, 0.0, 100.0, "", "", false}}; opt.set_bounds("x", -10.0, 100.0); GradientConfig grad_config; grad_config.learning_rate = 0.05; grad_config.max_iterations = 500; grad_config.tolerance = 1e-6; auto result = opt.optimize_gradient(initial, grad_config); for (const auto& p : result.optimal_params) { if (p.name == "x") { double val = std::get(p.value); EXPECT_NEAR(val, 3.0, 3.0); // 梯度下降有收敛性,容差较大 } } } TEST(KBETest, Optimization_Constraints) { ParametricOptimization opt; opt.set_fitness([](const std::vector& params) { for (const auto& p : params) { if (p.name == "x" && std::holds_alternative(p.value)) { double x = std::get(p.value); return -x; // 最小化 x(GA 最大化适应度 = 最小化 x) } } return 0.0; }); std::vector initial = {{"x", 5.0, 0.0, 100.0, "", "", false}}; // 用 bounds 约束 x 上限为 10 opt.set_bounds("x", 0.0, 10.0); GAConfig ga_config; ga_config.population_size = 30; ga_config.generations = 30; auto result = opt.optimize_ga(initial, ga_config); for (const auto& p : result.optimal_params) { if (p.name == "x") { double val = std::get(p.value); EXPECT_LE(val, 10.0); // 必须满足 bound 约束 EXPECT_GT(val, -1.0); // 不能为负 } } }