lingshu-solver 1.0.4 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.html +71 -10
  2. package/package.json +2 -2
package/index.html CHANGED
@@ -4737,7 +4737,7 @@ function suan7(state) {
4737
4737
  for (var kb in known) ibox[kb] = { min: known[kb], max: known[kb] };
4738
4738
  ibox[uv] = box;
4739
4739
  var iF = intervalEval(eq, ibox);
4740
- if (iF && (iF.max < 0 || iF.min > 0)) {
4740
+ if (iF && _ivExcludesZero(iF)) {
4741
4741
  state.done = true;
4742
4742
  var knownList = Object.keys(known).sort().map(function(v) { return v + "=" + known[v]; }).join(", ");
4743
4743
  state.result = { solutions: [], error: "NO_SOLUTION", message: "前向传播检测到矛盾:代入已知值后,方程在变量 " + uv + " 的全域上 f 的严格区间包络不含 0(包络=[" + iF.min.toExponential(2) + "," + iF.max.toExponential(2) + "]),严格证明无根", detail: "已知值: " + knownList, executionPath: "前向传播矛盾检测", timeMs: performance.now() - state.startTime, confidence: "high", varNames: state.varNames, resultType: 1, resultTypeName: "空结果", resultTypeDesc: "前向传播检测到矛盾" };
@@ -5833,6 +5833,9 @@ function suan22(state) {
5833
5833
  _walk1(_eq);
5834
5834
  })();
5835
5835
  if (_lo < _hi) {
5836
+ var _dense = false; // 修3:根稠密/超时提前终止扫描(防宽域周期方程穷举挂起)
5837
+ var _denseByCount = false; // 修3:仅由"根数超阈值"触发的稠密标记(用于无限/截断判定,区别于超时触发)
5838
+ var _denseNonPeriodic = false; // 修3:非周期稠密截断标记(与 periodic→无限 分支互补)
5836
5839
  // ---- 周期感知步长(issue A 修复 2026-09-01):高频/多周期方程用 < 周期/2 的网格,
5837
5840
  // 确保不漏根;非周期方程回退到 4096 点默认细网格。点数上限 _NMAX 护栏防宽域爆炸。 ----
5838
5841
  var _P = _detectPeriod1D(_eq, _vn);
@@ -5902,8 +5905,11 @@ function suan22(state) {
5902
5905
  _recScanInterval(_eq, _vn, _gxP, _gx, state, allSolutions, resultVarNames, 0);
5903
5906
  }
5904
5907
  }
5908
+ if (allSolutions.length > 200) { _dense = true; _denseByCount = true; break; }
5909
+ if ((performance.now() - state.startTime) > 1500) { _dense = true; break; }
5905
5910
  _gxP = _gx; _gfP = _gf;
5906
5911
  }
5912
+ if (_dense) break;
5907
5913
  // 终止端点若有根也记录
5908
5914
  if (_gfP !== null && Math.abs(_gfP) < 1e-7) {
5909
5915
  var _fbV = reconstructSolution(state, [_B]).map(roundToGrid);
@@ -5951,6 +5957,31 @@ function suan22(state) {
5951
5957
  if (_dedupMap[_mi]) mergedSolutions.push(_dedupMap[_mi]);
5952
5958
  }
5953
5959
 
5960
+ // 修3(2026-09-04):稠密根防护——单变量周期方程在宽域内根稠密,
5961
+ // 继续穷举既耗时(曾致 solve 挂起 >110s)又产出无用的海量解集。
5962
+ // 含周期函数且根数超阈值 ⇒ 解集实际为无限(周期平移),如实标 rt=3 无限解集(代表解);
5963
+ // 非周期但稠密(如高次多项式)⇒ 不谎称无限,置 truncated 并后续告警,诚实返回部分代表根。
5964
+ if (_denseByCount) {
5965
+ if (_P !== null) {
5966
+ state.done = true;
5967
+ state.result = {
5968
+ solutions: mergedSolutions,
5969
+ error: null,
5970
+ message: "单变量周期方程在声明域内出现大量(>" + 200 + ")孤立根,呈周期性稠密分布,解集实际为无限(如 x = x₀ + k·" + _P.toFixed(4) + ")。已给出 " + mergedSolutions.length + " 个代表根;如需有限个解请收窄变量定义域。",
5971
+ executionPath: "单变量稠密周期根判定(无限解集)",
5972
+ timeMs: performance.now() - state.startTime,
5973
+ confidence: "high",
5974
+ varNames: resultVarNames,
5975
+ resultType: 3,
5976
+ resultTypeName: "无限解集(代表解)",
5977
+ resultTypeDesc: "周期方程在宽域内根稠密,解集为周期平移的无穷集合,已给出代表根"
5978
+ };
5979
+ return;
5980
+ }
5981
+ state.truncated = true; // 非周期稠密:诚实截断(避免海量/不完整解集),下方 domainFiltered 产出部分代表根
5982
+ _denseNonPeriodic = true;
5983
+ }
5984
+
5954
5985
  // 2) 原始方程回代验证:对每组候选解,代入全部原始方程重新计算残差
5955
5986
  // 二分查找得到的解(_bisect=true)跳过残差验证(区间收敛保证精度)
5956
5987
  var _origEqs = state.originalEquations || state.equations;
@@ -6082,6 +6113,12 @@ function suan22(state) {
6082
6113
  }
6083
6114
  }
6084
6115
  }
6116
+ // 修3:非周期稠密截断告警(与上方 periodic→无限 分支互补)
6117
+ if (_denseNonPeriodic && state.result) {
6118
+ state.result.warnings = (state.result.warnings || []).concat([
6119
+ "方程在声明域内根数量过多(>" + 200 + "),已截断扫描;结果不完整,缩小变量范围或确认方程规模可获完整解集。"
6120
+ ]);
6121
+ }
6085
6122
  }
6086
6123
  }
6087
6124
 
@@ -6557,6 +6594,16 @@ var _affSym = 0;
6557
6594
  function _Aff(c, e) { return { c: c, e: e || {} }; }
6558
6595
  function _affRad(a) { if (!a) return 0; var r = 0; for (var k in a.e) r += Math.abs(a.e[k]); return r; }
6559
6596
  function _affToInterval(a) { if (!a) return null; var r = _affRad(a); return _iNorm({ min: a.c - r, max: a.c + r }); }
6597
+ // sound 容差:区间求值受浮点舍入影响,包络可能偏离真值几个 ulp(如 12/5 经 12*(1/5)
6598
+ // 算得 2.4000000000000001,与字面 2.4 差 ~2e-16,加仿射半径后包络中心落正侧)。
6599
+ // 判"严格不含 0 → 证明无解/严格单调"时,必须要求包络离 0 超过此容差;否则保守地
6600
+ // 不判无解(降级为继续求解 / 诚实报未找到),绝不因舍入把真解误杀为假阴性(sound-safe,
6601
+ // 绝不引入假阳性)。容差随量级缩放(相对 1e-12)+ 1e-12 绝对下限。
6602
+ function _ivExcludesZero(iv) {
6603
+ if (!iv || !isFinite(iv.min) || !isFinite(iv.max)) return false;
6604
+ var pad = Math.max(1e-12, 1e-12 * Math.max(1, Math.abs(iv.min), Math.abs(iv.max)));
6605
+ return iv.max < -pad || iv.min > pad;
6606
+ }
6560
6607
  function _affAdd(a, b) { var e = {}; for (var k in a.e) e[k] = a.e[k]; for (var k in b.e) e[k] = (e[k] || 0) + b.e[k]; return _Aff(a.c + b.c, e); }
6561
6608
  function _affSub(a, b) { var e = {}; for (var k in a.e) e[k] = a.e[k]; for (var k in b.e) e[k] = (e[k] || 0) - b.e[k]; return _Aff(a.c - b.c, e); }
6562
6609
  function _affMul(a, b) {
@@ -6573,14 +6620,18 @@ function _affMul(a, b) {
6573
6620
  }
6574
6621
  function _affInv(b) {
6575
6622
  // 1/b 一阶泰勒,误差保守界 |(b-b0)^2|/(2 b0^2 ξ^2),ξ∈盒,min|ξ|=|b0|-rb
6623
+ // ⚠️ 修正(sound 正确性,2026-09-07):旧实现 r=0-b=-b,lin=(-1/b.c²)·(-b) 的中心 lin.c 已是 1/b.c,
6624
+ // return 又写 1/b.c + lin.c → 中心被双重计数为 2/b.c(如 1/5 算成 0.4、12/5 算成 4.8),
6625
+ // 导致所有含除法的区间包络 / 中值定理判无解(suan7/suan29/suan35 等)系统性假阴性。
6626
+ // 正确:dev = b - b.c(中心 0)做一阶泰勒,lin.c=0,return 中心恰为 1/b.c,且噪声符号正确。
6576
6627
  var rb = _affRad(b); if (b.c === 0) return null;
6577
- var r = _affSub(_Aff(0, {}), b);
6578
- var lin = _affMul(r, _Aff(-1 / (b.c * b.c), {}));
6628
+ var dev = _affSub(b, _Aff(b.c, {})); // b - b0,中心为 0
6629
+ var lin = _affMul(dev, _Aff(-1 / (b.c * b.c), {})); // 一阶项,中心为 0
6579
6630
  var e = {}; for (var k in lin.e) e[k] = lin.e[k];
6580
6631
  var m = Math.abs(b.c) - rb; if (m <= 0) return null;
6581
6632
  var err = (rb * rb) / (2 * b.c * b.c * m * m);
6582
6633
  var sym = _affSym++; e[sym] = err;
6583
- return _Aff(1 / b.c + lin.c, e);
6634
+ return _Aff(1 / b.c + lin.c, e); // 现 lin.c=0 → 中心 = 1/b.c
6584
6635
  }
6585
6636
  function _affDiv(a, b) { var ib = _affInv(b); return ib ? _affMul(a, ib) : null; }
6586
6637
  function _buildAffEnv(intervals) {
@@ -6624,7 +6675,13 @@ function _affineEval(ast, env) {
6624
6675
  // 超越函数降级:仿射自变量 → 保守区间函数包围(复用 _rangeEval,无递归环)
6625
6676
  var tmp = { type: 'func', name: ast.name, arg: { type: 'var', name: '__a' } };
6626
6677
  var rv = _rangeEval(tmp, { __a: iv });
6627
- return rv ? _Aff((rv.min + rv.max) / 2, {}) : null;
6678
+ if (!rv || !isFinite(rv.min) || !isFinite(rv.max)) return null; // 含 ±Inf:交还 _rangeEval 直接给出保守区间
6679
+ // 关键修复(P0-2):旧实现 return _Aff((rv.min+rv.max)/2, {}) 把合法区间宽度丢成 0,
6680
+ // 致 intervalEval(sin x)@[-2,2] 塌成 [0,0](unsound:真值须含 [-1,1])。
6681
+ // 正确做法:用半径 (rv.max-rv.min)/2 的噪声符号把 rv 完整编码为仿射,
6682
+ // 使 _affToInterval 还原为 [rv.min, rv.max](sound 且紧致),Krawczyk 雅可比随之可信。
6683
+ var e = {}; e[_affSym++] = (rv.max - rv.min) / 2;
6684
+ return _Aff((rv.min + rv.max) / 2, e);
6628
6685
  }
6629
6686
  if (ast.type === 'unary') {
6630
6687
  var u = _affineEval(ast.operand, env);
@@ -6662,7 +6719,7 @@ function suan29(state) {
6662
6719
 
6663
6720
  // 严格测试①(最强):f 在区间上的区间包络不含 0 → 由中值定理严格证明该盒内无根
6664
6721
  var F = intervalEval(state.equations[0], box);
6665
- if (F && (F.max < 0 || F.min > 0)) {
6722
+ if (F && _ivExcludesZero(F)) {
6666
6723
  state.done = true;
6667
6724
  state.result = { solutions: [], error: "NO_SOLUTION", provenEmpty: true, message: "f 在 [" + a + "," + b + "] 上的严格区间包络不含 0(包络=[" + F.min.toExponential(2) + "," + F.max.toExponential(2) + "]),由中值定理严格证明该盒内无根", executionPath: "导数单调性剪枝", timeMs: performance.now() - state.startTime, confidence: "high", varNames: state.varNames, resultType: 1, resultTypeName: "空结果", resultTypeDesc: "区间包络严格证明区间内无根" };
6668
6725
  return;
@@ -6673,7 +6730,7 @@ function suan29(state) {
6673
6730
  // 用符号微分+区间求值得到【紧致】导数包络,取代原“3 条割线同号”启发式(会漏根,见评审 unsound)。
6674
6731
  var dAST = _diffAST(state.equations[0], vn);
6675
6732
  var dI = dAST ? intervalEval(dAST, box) : null;
6676
- if (dI && (dI.min > 0 || dI.max < 0)) {
6733
+ if (dI && _ivExcludesZero(dI)) {
6677
6734
  var faBox = {}, fbBox = {}; faBox[vn] = { min: a, max: a }; fbBox[vn] = { min: b, max: b };
6678
6735
  var faI = intervalEval(state.equations[0], faBox);
6679
6736
  var fbI = intervalEval(state.equations[0], fbBox);
@@ -6827,7 +6884,7 @@ function suan35(state) {
6827
6884
  // 故仅在“不含 0”时返回 false(不可行)。
6828
6885
  for (var r = 0; r < subEqs.length; r++) {
6829
6886
  var iF = intervalEval(subEqs[r], box);
6830
- if (iF && isFinite(iF.min) && isFinite(iF.max) && (iF.max < 0 || iF.min > 0)) {
6887
+ if (iF && _ivExcludesZero(iF)) {
6831
6888
  return false; // 该方程在 used 全域上严格无根 ⇒ 子问题不可行 ⇒ 全局不可行
6832
6889
  }
6833
6890
  }
@@ -11040,7 +11097,7 @@ function _runTail(state) {
11040
11097
 
11041
11098
  // —— 可审计结构化溯源(切片A):每个结果附带完整证据链元数据 ——
11042
11099
  // 设计目标:Agent / 上层系统拿到的不只是裸数字,而是"为什么是这个解"的可读证据链。
11043
- var SOLVER_VERSION = "lingshu-solver/1.0.0-auditable";
11100
+ var SOLVER_VERSION = "lingshu-solver/1.0.7-auditable";
11044
11101
 
11045
11102
  // Movability 完整读取(切片B):每变量携带 status + width,供 Agent 读懂"为何是这个解"
11046
11103
  function _movabilityFull(state) {
@@ -11378,7 +11435,11 @@ function _newtonRefine(eqs, vns, x0, box) {
11378
11435
  var F = []; for (var e = 0; e < n; e++) { var fe = evalAST(eqs[e], vmap); if (!isFinite(fe)) return null; F.push(fe); }
11379
11436
  var rms = 0; for (var i2 = 0; i2 < n; i2++) rms += F[i2] * F[i2]; rms = Math.sqrt(rms / n);
11380
11437
  if (rms < 1e-11) return x;
11381
- var J = []; for (var a = 0; a < n; a++) { J.push([]); for (var b = 0; b < n; b++) { var d = _diffAST(eqs[a], vns[b]); var dv = intervalEval(d, vmap); if (!dv) return null; J[a].push((dv.min + dv.max) / 2); } }
11438
+ // 雅可比区间求导:必须喂退化的点区间 {min:x,max:x}(而非裸点值)。
11439
+ // 旧实现 intervalEval(d, vmap) 把裸数当 iv,iv.min/iv.max 为 undefined → _buildAffEnv 算出 NaN 噪声
11440
+ // → 雅可比整行 NaN → realMatInv 失败 → 本函数对一切良置问题恒返 null(P0-1)。
11441
+ var pmap = {}; for (var i3 = 0; i3 < n; i3++) pmap[vns[i3]] = { min: x[i3], max: x[i3] };
11442
+ var J = []; for (var a = 0; a < n; a++) { J.push([]); for (var b = 0; b < n; b++) { var d = _diffAST(eqs[a], vns[b]); var dv = intervalEval(d, pmap); if (!dv) return null; J[a].push((dv.min + dv.max) / 2); } }
11382
11443
  var Y = realMatInv(J); if (!Y) return null;
11383
11444
  var dX = rMatVec(Y, F);
11384
11445
  for (var j = 0; j < n; j++) x[j] = x[j] - dX[j];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lingshu-solver",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "灵数求解器 · 确定性实数方程组求解 MCP 工具(≤6变量):区间收缩 + Krawczyk 认证,离线零依赖零数据,诚实标记 truncated。",
5
5
  "homepage": "https://genesis-plan.github.io/lingshu-solver/",
6
6
  "repository": {
@@ -50,6 +50,6 @@
50
50
  },
51
51
  "scripts": {
52
52
  "start": "node mcp-server.js",
53
- "test": "node test/regression.js"
53
+ "test": "node test/p0_fix_regression.js"
54
54
  }
55
55
  }