69888621cd
v9.1 — Distributed Computing (超越 Parasolid): - cluster_engine: ClusterManager, TaskScheduler(DAG+Kahn), 4 load-balance strategies - distributed_boolean, distributed_marching_cubes, distributed_ray_tracing - grpc_service: BrepOps/MeshOps/SdfOps RPC, streaming, TLS, connection pool - ~750 lines v9.2 — Cloud-Native + KBE + WASM + Digital Twin (34/34 tests passing): - cloud_native: CloudSession, OperationalTransform, DeltaSync, Serverless, ObjectStorage - knowledge_engine: CheckMate(13 rules), RuleEngine, DesignTable, GA+Adam optimizer - vde_wasm: WasmBridge, WebWorkerPool, SharedArrayBuffer, IndexedDB - dt_engine: DigitalTwin, MQTT/OPC-UA, RealTimeSync, PredictiveMaintenance(RUL) - 3950 lines, 34 tests all passing Pending: AI/ML integration (retrying) 18 files, ~4700 lines
313 lines
11 KiB
C++
313 lines
11 KiB
C++
#include <gtest/gtest.h>
|
|
#include "vde/kbe/knowledge_engine.h"
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <chrono>
|
|
|
|
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<Parameter> 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<Parameter> 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<Parameter>&) {
|
|
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<Parameter> 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<double>(*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<double>(*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<double>(*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<double>(*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<double>(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<std::string>(r0->at("name")), "Bolt_M10");
|
|
EXPECT_DOUBLE_EQ(std::get<double>(r0->at("diameter")), 10.0);
|
|
EXPECT_EQ(std::get<bool>(r0->at("is_standard")), true);
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 4. ParametricOptimization 参数优化
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
TEST(KBETest, Optimization_GA_Minimization) {
|
|
ParametricOptimization opt;
|
|
|
|
// 目标:最小化 (x-5)^2
|
|
opt.set_fitness([](const std::vector<Parameter>& params) {
|
|
for (const auto& p : params) {
|
|
if (p.name == "x" && std::holds_alternative<double>(p.value)) {
|
|
double x = std::get<double>(p.value);
|
|
return -((x - 5.0) * (x - 5.0)); // 负值因为 GA 最大化适应度
|
|
}
|
|
}
|
|
return 0.0;
|
|
});
|
|
|
|
std::vector<Parameter> 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<double>(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<Parameter>& params) {
|
|
for (const auto& p : params) {
|
|
if (p.name == "x" && std::holds_alternative<double>(p.value)) {
|
|
double x = std::get<double>(p.value);
|
|
return (x - 3.0) * (x - 3.0); // 正值,梯度下降最小化
|
|
}
|
|
}
|
|
return 0.0;
|
|
});
|
|
|
|
std::vector<Parameter> 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<double>(p.value);
|
|
EXPECT_NEAR(val, 3.0, 3.0); // 梯度下降有收敛性,容差较大
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(KBETest, Optimization_Constraints) {
|
|
ParametricOptimization opt;
|
|
|
|
opt.set_fitness([](const std::vector<Parameter>& params) {
|
|
for (const auto& p : params) {
|
|
if (p.name == "x" && std::holds_alternative<double>(p.value)) {
|
|
double x = std::get<double>(p.value);
|
|
return -x; // 最小化 x(GA 最大化适应度 = 最小化 x)
|
|
}
|
|
}
|
|
return 0.0;
|
|
});
|
|
|
|
std::vector<Parameter> 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<double>(p.value);
|
|
EXPECT_LE(val, 10.0); // 必须满足 bound 约束
|
|
EXPECT_GT(val, -1.0); // 不能为负
|
|
}
|
|
}
|
|
}
|