universal-physics-tensor 11.0.0 → 12.0.0

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 (287) hide show
  1. package/README.md +36 -34
  2. package/data/applied-cases.json +1892 -0
  3. package/data/applied-cases.schema.json +194 -0
  4. package/data/atlas/atlas.json +21 -21
  5. package/data/atlas/atlas.jsonld +1 -1
  6. package/data/atlas/diffusion.json +5 -5
  7. package/data/atlas/oscillators.json +7 -7
  8. package/data/atlas/waves.json +11 -11
  9. package/data/bridge-catalog.json +20 -4
  10. package/data/units.json +6 -25
  11. package/dist/atlas/benchmark/stats.d.ts.map +1 -1
  12. package/dist/atlas/benchmark/stats.js +6 -20
  13. package/dist/atlas/benchmark/stats.js.map +1 -1
  14. package/dist/atlas/physjs-entries.generated.d.ts +13 -4
  15. package/dist/atlas/physjs-entries.generated.d.ts.map +1 -1
  16. package/dist/atlas/physjs-entries.generated.js +15 -4
  17. package/dist/atlas/physjs-entries.generated.js.map +1 -1
  18. package/dist/atlas/physjs-reviewed.js +2 -2
  19. package/dist/atlas/physjs-reviewed.js.map +1 -1
  20. package/dist/cases/catalog-checks.d.ts +10 -0
  21. package/dist/cases/catalog-checks.d.ts.map +1 -0
  22. package/dist/cases/catalog-checks.js +241 -0
  23. package/dist/cases/catalog-checks.js.map +1 -0
  24. package/dist/cases/catalog.d.ts +83 -0
  25. package/dist/cases/catalog.d.ts.map +1 -0
  26. package/dist/cases/catalog.js +118 -0
  27. package/dist/cases/catalog.js.map +1 -0
  28. package/dist/cases/engine.d.ts +13 -0
  29. package/dist/cases/engine.d.ts.map +1 -0
  30. package/dist/cases/engine.js +179 -0
  31. package/dist/cases/engine.js.map +1 -0
  32. package/dist/cases/index.d.ts.map +1 -1
  33. package/dist/cases/index.js +5 -7
  34. package/dist/cases/index.js.map +1 -1
  35. package/dist/cases/methods.d.ts +215 -0
  36. package/dist/cases/methods.d.ts.map +1 -0
  37. package/dist/cases/methods.js +743 -0
  38. package/dist/cases/methods.js.map +1 -0
  39. package/dist/cases/types.d.ts +12 -0
  40. package/dist/cases/types.d.ts.map +1 -1
  41. package/dist/cases/types.js.map +1 -1
  42. package/dist/cli/args.d.ts +2 -0
  43. package/dist/cli/args.d.ts.map +1 -1
  44. package/dist/cli/args.js.map +1 -1
  45. package/dist/cli/command.d.ts +3 -0
  46. package/dist/cli/command.d.ts.map +1 -1
  47. package/dist/cli/command.js.map +1 -1
  48. package/dist/cli/commands/_discovery-opts.d.ts +1 -0
  49. package/dist/cli/commands/_discovery-opts.d.ts.map +1 -1
  50. package/dist/cli/commands/_discovery-opts.js +1 -0
  51. package/dist/cli/commands/_discovery-opts.js.map +1 -1
  52. package/dist/cli/commands/discover.d.ts.map +1 -1
  53. package/dist/cli/commands/discover.js +116 -71
  54. package/dist/cli/commands/discover.js.map +1 -1
  55. package/dist/cli/commands/evaluate.js +2 -2
  56. package/dist/cli/commands/evaluate.js.map +1 -1
  57. package/dist/cli/commands/explain.d.ts.map +1 -1
  58. package/dist/cli/commands/explain.js +22 -2
  59. package/dist/cli/commands/explain.js.map +1 -1
  60. package/dist/cli/commands/ground.d.ts.map +1 -1
  61. package/dist/cli/commands/ground.js +6 -1
  62. package/dist/cli/commands/ground.js.map +1 -1
  63. package/dist/cli/commands/map.d.ts.map +1 -1
  64. package/dist/cli/commands/map.js +9 -6
  65. package/dist/cli/commands/map.js.map +1 -1
  66. package/dist/cli/determination.d.ts +1 -0
  67. package/dist/cli/determination.d.ts.map +1 -1
  68. package/dist/cli/determination.js.map +1 -1
  69. package/dist/cli/flag-help.d.ts +1 -0
  70. package/dist/cli/flag-help.d.ts.map +1 -1
  71. package/dist/cli/flag-help.js +1 -0
  72. package/dist/cli/flag-help.js.map +1 -1
  73. package/dist/cli/graphs.d.ts +2 -0
  74. package/dist/cli/graphs.d.ts.map +1 -1
  75. package/dist/cli/graphs.js +1 -0
  76. package/dist/cli/graphs.js.map +1 -1
  77. package/dist/cli/main.d.ts +1 -0
  78. package/dist/cli/main.d.ts.map +1 -1
  79. package/dist/cli/main.js +1 -0
  80. package/dist/cli/main.js.map +1 -1
  81. package/dist/cli/output.d.ts +8 -0
  82. package/dist/cli/output.d.ts.map +1 -1
  83. package/dist/cli/output.js +8 -0
  84. package/dist/cli/output.js.map +1 -1
  85. package/dist/cli/statuses.d.ts.map +1 -1
  86. package/dist/cli/statuses.js +12 -2
  87. package/dist/cli/statuses.js.map +1 -1
  88. package/dist/cli/version.d.ts +1 -0
  89. package/dist/cli/version.d.ts.map +1 -1
  90. package/dist/cli/version.js +1 -0
  91. package/dist/cli/version.js.map +1 -1
  92. package/dist/cli-api.d.ts +2 -0
  93. package/dist/cli-api.d.ts.map +1 -1
  94. package/dist/cli-api.js +2 -0
  95. package/dist/cli-api.js.map +1 -1
  96. package/dist/composition/canonical-graph.d.ts +2 -2
  97. package/dist/composition/canonical-graph.js +2 -2
  98. package/dist/composition/canonical-graph.js.map +1 -1
  99. package/dist/composition/consequence.d.ts +15 -5
  100. package/dist/composition/consequence.d.ts.map +1 -1
  101. package/dist/composition/consequence.js +6 -4
  102. package/dist/composition/consequence.js.map +1 -1
  103. package/dist/composition/discovery.d.ts +78 -14
  104. package/dist/composition/discovery.d.ts.map +1 -1
  105. package/dist/composition/discovery.js +73 -23
  106. package/dist/composition/discovery.js.map +1 -1
  107. package/dist/composition/expr-eval.d.ts +1 -1
  108. package/dist/composition/expr-eval.d.ts.map +1 -1
  109. package/dist/composition/expr-eval.js +4 -4
  110. package/dist/composition/expr-eval.js.map +1 -1
  111. package/dist/composition/expr-simplify.d.ts +10 -14
  112. package/dist/composition/expr-simplify.d.ts.map +1 -1
  113. package/dist/composition/expr-simplify.js +49 -52
  114. package/dist/composition/expr-simplify.js.map +1 -1
  115. package/dist/composition/grounding.d.ts.map +1 -1
  116. package/dist/composition/grounding.js +19 -7
  117. package/dist/composition/grounding.js.map +1 -1
  118. package/dist/composition/mathts-quiet.d.ts +4 -6
  119. package/dist/composition/mathts-quiet.d.ts.map +1 -1
  120. package/dist/composition/mathts-quiet.js +4 -14
  121. package/dist/composition/mathts-quiet.js.map +1 -1
  122. package/dist/composition/own-equation-magnitude.d.ts +95 -0
  123. package/dist/composition/own-equation-magnitude.d.ts.map +1 -0
  124. package/dist/composition/own-equation-magnitude.js +270 -0
  125. package/dist/composition/own-equation-magnitude.js.map +1 -0
  126. package/dist/composition/probe/backend-protocol.d.ts +3 -0
  127. package/dist/composition/probe/backend-protocol.d.ts.map +1 -1
  128. package/dist/composition/probe/backend-protocol.js.map +1 -1
  129. package/dist/composition/probe/candidate-store.d.ts +1 -0
  130. package/dist/composition/probe/candidate-store.d.ts.map +1 -1
  131. package/dist/composition/probe/candidate-store.js +1 -0
  132. package/dist/composition/probe/candidate-store.js.map +1 -1
  133. package/dist/composition/probe/corpus.d.ts +4 -2
  134. package/dist/composition/probe/corpus.d.ts.map +1 -1
  135. package/dist/composition/probe/corpus.js +3 -3
  136. package/dist/composition/probe/corpus.js.map +1 -1
  137. package/dist/composition/probe/dataset.d.ts +1 -0
  138. package/dist/composition/probe/dataset.d.ts.map +1 -1
  139. package/dist/composition/probe/dataset.js.map +1 -1
  140. package/dist/composition/probe/experiment-design.d.ts +2 -0
  141. package/dist/composition/probe/experiment-design.d.ts.map +1 -1
  142. package/dist/composition/probe/experiment-design.js.map +1 -1
  143. package/dist/composition/probe/falsify.d.ts +2 -0
  144. package/dist/composition/probe/falsify.d.ts.map +1 -1
  145. package/dist/composition/probe/falsify.js.map +1 -1
  146. package/dist/composition/probe/fit.d.ts +1 -0
  147. package/dist/composition/probe/fit.d.ts.map +1 -1
  148. package/dist/composition/probe/fit.js.map +1 -1
  149. package/dist/composition/probe/generator.d.ts +3 -0
  150. package/dist/composition/probe/generator.d.ts.map +1 -1
  151. package/dist/composition/probe/generator.js +5 -6
  152. package/dist/composition/probe/generator.js.map +1 -1
  153. package/dist/composition/probe/limits.d.ts +1 -0
  154. package/dist/composition/probe/limits.d.ts.map +1 -1
  155. package/dist/composition/probe/limits.js.map +1 -1
  156. package/dist/composition/probe/pipeline.d.ts +5 -0
  157. package/dist/composition/probe/pipeline.d.ts.map +1 -1
  158. package/dist/composition/probe/pipeline.js +64 -21
  159. package/dist/composition/probe/pipeline.js.map +1 -1
  160. package/dist/composition/probe/power-law.d.ts.map +1 -1
  161. package/dist/composition/probe/power-law.js +3 -7
  162. package/dist/composition/probe/power-law.js.map +1 -1
  163. package/dist/composition/probe/problem.d.ts +1 -0
  164. package/dist/composition/probe/problem.d.ts.map +1 -1
  165. package/dist/composition/probe/problem.js.map +1 -1
  166. package/dist/composition/probe/residual.d.ts +1 -0
  167. package/dist/composition/probe/residual.d.ts.map +1 -1
  168. package/dist/composition/probe/residual.js +1 -0
  169. package/dist/composition/probe/residual.js.map +1 -1
  170. package/dist/composition/probe/run-manifest.d.ts +1 -0
  171. package/dist/composition/probe/run-manifest.d.ts.map +1 -1
  172. package/dist/composition/probe/run-manifest.js.map +1 -1
  173. package/dist/composition/probe/scalar-search.d.ts +50 -0
  174. package/dist/composition/probe/scalar-search.d.ts.map +1 -0
  175. package/dist/composition/probe/scalar-search.js +169 -0
  176. package/dist/composition/probe/scalar-search.js.map +1 -0
  177. package/dist/composition/probe/scoring.d.ts +1 -0
  178. package/dist/composition/probe/scoring.d.ts.map +1 -1
  179. package/dist/composition/probe/scoring.js.map +1 -1
  180. package/dist/composition/probe/study.d.ts +2 -2
  181. package/dist/composition/probe/study.d.ts.map +1 -1
  182. package/dist/composition/probe/study.js +6 -10
  183. package/dist/composition/probe/study.js.map +1 -1
  184. package/dist/composition/probe/t-tail.d.ts +3 -4
  185. package/dist/composition/probe/t-tail.d.ts.map +1 -1
  186. package/dist/composition/probe/t-tail.js +7 -10
  187. package/dist/composition/probe/t-tail.js.map +1 -1
  188. package/dist/composition/proposed-bridges.d.ts +11 -2
  189. package/dist/composition/proposed-bridges.d.ts.map +1 -1
  190. package/dist/composition/proposed-bridges.js +7 -5
  191. package/dist/composition/proposed-bridges.js.map +1 -1
  192. package/dist/composition/rational-exponent.d.ts +2 -2
  193. package/dist/composition/rational-exponent.d.ts.map +1 -1
  194. package/dist/composition/rational-exponent.js +4 -4
  195. package/dist/composition/rational-exponent.js.map +1 -1
  196. package/dist/composition/representative-values.d.ts.map +1 -1
  197. package/dist/composition/representative-values.js +6 -0
  198. package/dist/composition/representative-values.js.map +1 -1
  199. package/dist/composition/retrodiction.d.ts +15 -0
  200. package/dist/composition/retrodiction.d.ts.map +1 -1
  201. package/dist/composition/retrodiction.js +10 -3
  202. package/dist/composition/retrodiction.js.map +1 -1
  203. package/dist/composition/user-equation.d.ts +1 -0
  204. package/dist/composition/user-equation.d.ts.map +1 -1
  205. package/dist/composition/user-equation.js +1 -0
  206. package/dist/composition/user-equation.js.map +1 -1
  207. package/dist/core/constants.d.ts +12 -11
  208. package/dist/core/constants.d.ts.map +1 -1
  209. package/dist/core/constants.js +24 -21
  210. package/dist/core/constants.js.map +1 -1
  211. package/dist/diff/bridge-ast-gradient.d.ts +27 -23
  212. package/dist/diff/bridge-ast-gradient.d.ts.map +1 -1
  213. package/dist/diff/bridge-ast-gradient.js +75 -173
  214. package/dist/diff/bridge-ast-gradient.js.map +1 -1
  215. package/dist/dimensional/ast-types.d.ts +15 -0
  216. package/dist/dimensional/ast-types.d.ts.map +1 -1
  217. package/dist/dimensional/ast-types.js.map +1 -1
  218. package/dist/dimensional/curvature-composite.d.ts +1 -1
  219. package/dist/dimensional/curvature-composite.js.map +1 -1
  220. package/dist/dimensional/exact-scale.d.ts +41 -11
  221. package/dist/dimensional/exact-scale.d.ts.map +1 -1
  222. package/dist/dimensional/exact-scale.js +115 -123
  223. package/dist/dimensional/exact-scale.js.map +1 -1
  224. package/dist/dimensional/mathts-unit-delegation.d.ts +29 -0
  225. package/dist/dimensional/mathts-unit-delegation.d.ts.map +1 -0
  226. package/dist/dimensional/mathts-unit-delegation.js +187 -0
  227. package/dist/dimensional/mathts-unit-delegation.js.map +1 -0
  228. package/dist/dimensional/mathts-unit-wrapper.d.ts +33 -0
  229. package/dist/dimensional/mathts-unit-wrapper.d.ts.map +1 -0
  230. package/dist/dimensional/mathts-unit-wrapper.js +190 -0
  231. package/dist/dimensional/mathts-unit-wrapper.js.map +1 -0
  232. package/dist/dimensional/scalar-lowering.d.ts +21 -0
  233. package/dist/dimensional/scalar-lowering.d.ts.map +1 -0
  234. package/dist/dimensional/scalar-lowering.js +45 -0
  235. package/dist/dimensional/scalar-lowering.js.map +1 -0
  236. package/dist/dimensional/unit-data.d.ts +5 -1
  237. package/dist/dimensional/unit-data.d.ts.map +1 -1
  238. package/dist/dimensional/unit-data.js +28 -4
  239. package/dist/dimensional/unit-data.js.map +1 -1
  240. package/dist/dimensional/units.d.ts +2 -0
  241. package/dist/dimensional/units.d.ts.map +1 -1
  242. package/dist/dimensional/units.js +59 -3
  243. package/dist/dimensional/units.js.map +1 -1
  244. package/dist/dimensional/validator.d.ts +2 -0
  245. package/dist/dimensional/validator.d.ts.map +1 -1
  246. package/dist/dimensional/validator.js.map +1 -1
  247. package/dist/index.js.map +1 -1
  248. package/dist/numerical/connection-lowering-helpers.d.ts +3 -1
  249. package/dist/numerical/connection-lowering-helpers.d.ts.map +1 -1
  250. package/dist/numerical/connection-lowering-helpers.js +11 -15
  251. package/dist/numerical/connection-lowering-helpers.js.map +1 -1
  252. package/dist/numerical/pderiv.d.ts.map +1 -1
  253. package/dist/numerical/pderiv.js +2 -6
  254. package/dist/numerical/pderiv.js.map +1 -1
  255. package/dist/numerical/quadrature.js +3 -3
  256. package/dist/numerical/quadrature.js.map +1 -1
  257. package/dist/numerical/statistics.js +1 -1
  258. package/dist/numerical/statistics.js.map +1 -1
  259. package/dist/numerical/strides.d.ts +3 -2
  260. package/dist/numerical/strides.d.ts.map +1 -1
  261. package/dist/numerical/strides.js +3 -10
  262. package/dist/numerical/strides.js.map +1 -1
  263. package/package.json +9 -12
  264. package/dist/cases/brownian-sphere.d.ts +0 -68
  265. package/dist/cases/brownian-sphere.d.ts.map +0 -1
  266. package/dist/cases/brownian-sphere.js +0 -355
  267. package/dist/cases/brownian-sphere.js.map +0 -1
  268. package/dist/cases/damped-resonator.d.ts +0 -23
  269. package/dist/cases/damped-resonator.d.ts.map +0 -1
  270. package/dist/cases/damped-resonator.js +0 -221
  271. package/dist/cases/damped-resonator.js.map +0 -1
  272. package/dist/cases/kepler-rv.d.ts +0 -13
  273. package/dist/cases/kepler-rv.d.ts.map +0 -1
  274. package/dist/cases/kepler-rv.js +0 -173
  275. package/dist/cases/kepler-rv.js.map +0 -1
  276. package/dist/cases/lumped-cooling.d.ts +0 -51
  277. package/dist/cases/lumped-cooling.d.ts.map +0 -1
  278. package/dist/cases/lumped-cooling.js +0 -259
  279. package/dist/cases/lumped-cooling.js.map +0 -1
  280. package/dist/cases/resistor-noise.d.ts +0 -14
  281. package/dist/cases/resistor-noise.d.ts.map +0 -1
  282. package/dist/cases/resistor-noise.js +0 -215
  283. package/dist/cases/resistor-noise.js.map +0 -1
  284. package/dist/cases/skin-depth.d.ts +0 -17
  285. package/dist/cases/skin-depth.d.ts.map +0 -1
  286. package/dist/cases/skin-depth.js +0 -170
  287. package/dist/cases/skin-depth.js.map +0 -1
@@ -1,25 +1,24 @@
1
1
  /**
2
- * Exact bridge-gradients via reverse-mode AD over the symbolic RHS AST.
2
+ * Exact bridge-gradients over the symbolic RHS AST.
3
3
  *
4
4
  * The catalog's plain-JS evaluators cannot be differentiated by AD (they run
5
5
  * raw `Math.*` arithmetic and return a number, which carries no tape; see
6
6
  * `bridge-gradient.ts`). But each bridge also carries a faithful symbolic
7
7
  * encoding of its RHS (`*_RHS: ExprNode`). This module lowers that scalar AST
8
- * through `@danielsimonjr/mathts-autograd`'s `TapedTensor` ops — binding the
9
- * chosen variable to a traced input and every other symbol to a constant tape
10
- * leaf — so `reverseGrad` returns the EXACT derivative (machine precision), with
11
- * the plain-JS evaluators left untouched (P8 Decision #1).
8
+ * with the MathTS scalar builder and differentiates it with MathTS
9
+ * `compileScalarGradient`: the chosen variable is the one parameter, and every
10
+ * other symbol is a constant. The result is the EXACT derivative (machine
11
+ * precision); the plain-JS evaluators stay untouched (P8 Decision #1).
12
12
  *
13
- * Scope: the dimensional scalar grammar is `symbol` + `op(+ − * / ^)` (the
14
- * transcendentals of a bridge are absorbed into typed-stub *symbols*, so they
15
- * appear as opaque constants here). A bridge encoded with a typed stub therefore
16
- * differentiates with respect to the stub, not the physics inside it — faithful,
17
- * fully-expanded encodings (e.g. BE-42 `ℏc³/(8πGM·k_B)`) differentiate exactly.
18
- * `^` exponents must be constant (the differentiation variable may not appear in
19
- * an exponent).
13
+ * Scope: the scalar grammar is `symbol` + `op(+ − * / ^)` + `transcendental` +
14
+ * `abs`. A bridge encoded with a typed stub differentiates with respect to the
15
+ * stub, not the physics inside it; fully-expanded encodings (e.g. BE-42
16
+ * `ℏc³/(8πGM·k_B)`) differentiate exactly. A `^` exponent must be constant: a
17
+ * UPT pre-check refuses the differentiation variable in an exponent before
18
+ * MathTS sees the tree.
20
19
  *
21
- * Loads `@danielsimonjr/mathts-autograd` at the call and throws
22
- * `EngineCapabilityError` when that import fails.
20
+ * A point where the value overflows gives `NaN` for the value and the
21
+ * gradient (MathTS marks the row invalid), not `±Infinity`.
23
22
  *
24
23
  * @module diff/bridge-ast-gradient
25
24
  */
@@ -35,31 +34,36 @@ export interface ASTGradientResult {
35
34
  readonly gradient: number;
36
35
  }
37
36
  /**
38
- * Differentiate a scalar bridge RHS AST with respect to one symbol via
39
- * reverse-mode AD. Every symbol other than `varName` is resolved to a constant
40
- * (caller `bindings`, then {@link NAMED_CONSTANTS}, then numeric literal); the
41
- * `bindings` must contain `varName` (its value sets the point of evaluation).
37
+ * Differentiate a scalar bridge RHS AST with respect to one symbol through
38
+ * MathTS `compileScalarGradient`. Every symbol other than `varName` is resolved
39
+ * to a constant (caller `bindings`, then {@link NAMED_CONSTANTS}, then numeric
40
+ * literal); the `bindings` must contain `varName` (its value sets the point of
41
+ * evaluation).
42
42
  *
43
+ * @throws TypeError when `varName` is not bound, a symbol cannot be resolved,
44
+ * `varName` appears in an exponent, or a node kind is outside the scalar grammar.
43
45
  * @public
44
46
  */
45
- export declare function bridgeGradientAST(rhs: ExprNode, varName: string, bindings: Record<string, number>): Promise<ASTGradientResult>;
47
+ export declare function bridgeGradientAST(rhs: ExprNode, varName: string, bindings: Record<string, number>): ASTGradientResult;
46
48
  /**
47
49
  * Convenience wrapper around {@link bridgeGradientAST} that looks the RHS AST up
48
50
  * by catalog bridge id. `bridgeId` accepts a number, `'42'`, or `'BE-42'`.
49
51
  *
50
52
  * Throws if the id has no encoded RHS in {@link BRIDGE_RHS_BY_ID} (e.g. BE-51/52,
51
53
  * which are closed-form evaluators without an AST). If the bridge's RHS is not in
52
- * the differentiable scalar grammar (a tensor/integral/curvature encoding), the
54
+ * the differentiable scalar grammar (a tensor, integral or curvature encoding), the
53
55
  * delegated `bridgeGradientAST` throws the "unsupported node kind" error — use
54
56
  * {@link astDifferentiableBridgeIds} to discover which ids are AD-eligible.
55
57
  *
56
58
  * @public
57
59
  */
58
- export declare function bridgeGradientASTById(bridgeId: number | string, varName: string, bindings: Record<string, number>): Promise<ASTGradientResult>;
60
+ export declare function bridgeGradientASTById(bridgeId: number | string, varName: string, bindings: Record<string, number>): ASTGradientResult;
59
61
  /**
60
62
  * The catalog bridge ids whose encoded RHS lies entirely in the differentiable
61
- * scalar grammar — i.e. those `bridgeGradientASTById` can differentiate exactly.
62
- * Bridges encoded with tensor/integral/curvature nodes (or whose physics is
63
+ * scalar grammar. `bridgeGradientASTById` differentiates each of them exactly
64
+ * with respect to any variable that is not in an exponent; a variable in an
65
+ * exponent is refused (`checkExponents`).
66
+ * Bridges encoded with tensor, integral or curvature nodes (or whose physics is
63
67
  * hidden behind a typed-stub symbol) are excluded from EXACT differentiation
64
68
  * (a stub still differentiates w.r.t. the stub symbol, but not the physics
65
69
  * inside it). Returned sorted ascending.
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-ast-gradient.d.ts","sourceRoot":"","sources":["../../src/diff/bridge-ast-gradient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,6BAA6B,CAAC;AAM9E;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AA2ID;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAgI5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAU5B;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,EAAE,CAMrD"}
1
+ {"version":3,"file":"bridge-ast-gradient.d.ts","sourceRoot":"","sources":["../../src/diff/bridge-ast-gradient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAM5D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAgFD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,iBAAiB,CAkCnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,MAAM,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,iBAAiB,CAUnB;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,EAAE,CAMrD"}
@@ -1,69 +1,45 @@
1
1
  /**
2
- * Exact bridge-gradients via reverse-mode AD over the symbolic RHS AST.
2
+ * Exact bridge-gradients over the symbolic RHS AST.
3
3
  *
4
4
  * The catalog's plain-JS evaluators cannot be differentiated by AD (they run
5
5
  * raw `Math.*` arithmetic and return a number, which carries no tape; see
6
6
  * `bridge-gradient.ts`). But each bridge also carries a faithful symbolic
7
7
  * encoding of its RHS (`*_RHS: ExprNode`). This module lowers that scalar AST
8
- * through `@danielsimonjr/mathts-autograd`'s `TapedTensor` ops — binding the
9
- * chosen variable to a traced input and every other symbol to a constant tape
10
- * leaf — so `reverseGrad` returns the EXACT derivative (machine precision), with
11
- * the plain-JS evaluators left untouched (P8 Decision #1).
8
+ * with the MathTS scalar builder and differentiates it with MathTS
9
+ * `compileScalarGradient`: the chosen variable is the one parameter, and every
10
+ * other symbol is a constant. The result is the EXACT derivative (machine
11
+ * precision); the plain-JS evaluators stay untouched (P8 Decision #1).
12
12
  *
13
- * Scope: the dimensional scalar grammar is `symbol` + `op(+ − * / ^)` (the
14
- * transcendentals of a bridge are absorbed into typed-stub *symbols*, so they
15
- * appear as opaque constants here). A bridge encoded with a typed stub therefore
16
- * differentiates with respect to the stub, not the physics inside it — faithful,
17
- * fully-expanded encodings (e.g. BE-42 `ℏc³/(8πGM·k_B)`) differentiate exactly.
18
- * `^` exponents must be constant (the differentiation variable may not appear in
19
- * an exponent).
13
+ * Scope: the scalar grammar is `symbol` + `op(+ − * / ^)` + `transcendental` +
14
+ * `abs`. A bridge encoded with a typed stub differentiates with respect to the
15
+ * stub, not the physics inside it; fully-expanded encodings (e.g. BE-42
16
+ * `ℏc³/(8πGM·k_B)`) differentiate exactly. A `^` exponent must be constant: a
17
+ * UPT pre-check refuses the differentiation variable in an exponent before
18
+ * MathTS sees the tree.
20
19
  *
21
- * Loads `@danielsimonjr/mathts-autograd` at the call and throws
22
- * `EngineCapabilityError` when that import fails.
20
+ * A point where the value overflows gives `NaN` for the value and the
21
+ * gradient (MathTS marks the row invalid), not `±Infinity`.
23
22
  *
24
23
  * @module diff/bridge-ast-gradient
25
24
  */
25
+ import { ScalarBuildError } from '@danielsimonjr/mathts-expression';
26
+ import { compileScalarGradient } from '@danielsimonjr/mathts-functions';
27
+ import { forEachScalarLeaf, isDecimalLiteral } from '../dimensional/ast-leaves.js';
26
28
  import { constantScope } from '../dimensional/symbolic-constants.js';
27
- import { EngineCapabilityError } from '../numerical/errors.js';
29
+ import { toScalarNode } from '../dimensional/scalar-lowering.js';
28
30
  import { BRIDGE_RHS_BY_ID, parseBridgeId } from '../bridges/rhs-registry.js';
29
- import { GAUSS_LEGENDRE_16 } from '../numerical/quadrature.js';
30
- /** Map a transcendental fn name to the TapedTensor method that traces it. */
31
- function applyTranscendental(fn, t) {
32
- switch (fn) {
33
- case 'exp':
34
- return t.exp();
35
- case 'ln':
36
- return t.log();
37
- case 'log2':
38
- return t.log2();
39
- case 'log10':
40
- return t.log10();
41
- case 'sin':
42
- return t.sin();
43
- case 'cos':
44
- return t.cos();
45
- case 'tan':
46
- return t.tan();
47
- case 'sinh':
48
- return t.sinh();
49
- case 'cosh':
50
- return t.cosh();
51
- case 'tanh':
52
- return t.tanh();
53
- }
54
- }
55
31
  /**
56
32
  * Named physical constants the bridge encodings reference as bare symbols:
57
33
  * every registered spelling, from the constant registry, plus π and its
58
- * registered multiples, so a traced AST evaluates to the SAME number as the
34
+ * registered multiples, so the AST evaluates to the SAME number as the
59
35
  * numeric evaluator. Callers can override or extend via the `bindings` argument.
60
36
  */
61
37
  const NAMED_CONSTANTS = constantScope();
62
38
  /** Resolve a symbol's numeric value: caller bindings → named constants → numeric literal. */
63
39
  function resolveConstant(name, bindings) {
64
- if (Object.prototype.hasOwnProperty.call(bindings, name))
40
+ if (Object.hasOwn(bindings, name))
65
41
  return bindings[name];
66
- if (Object.prototype.hasOwnProperty.call(NAMED_CONSTANTS, name))
42
+ if (Object.hasOwn(NAMED_CONSTANTS, name))
67
43
  return NAMED_CONSTANTS[name];
68
44
  const asNumber = Number(name);
69
45
  if (name.trim() !== '' && Number.isFinite(asNumber))
@@ -75,9 +51,8 @@ function resolveConstant(name, bindings) {
75
51
  *
76
52
  * The exponent may contain scalar arithmetic over constants/bindings (for
77
53
  * example `-1/z`) as long as it is independent of the differentiation
78
- * variable. Variable exponents require a traced `pow(base, exponent)` op,
79
- * which the current MathTS tape surface does not expose, so they are rejected
80
- * explicitly rather than being silently detached from AD.
54
+ * variable. UPT keeps the constant-power contract, so the differentiation
55
+ * variable in an exponent is refused before MathTS reads the tree.
81
56
  */
82
57
  function resolveExponent(node, varName, bindings) {
83
58
  if (node.kind === 'symbol') {
@@ -103,125 +78,60 @@ function resolveExponent(node, varName, bindings) {
103
78
  throw new TypeError(`bridgeGradientAST: '^' exponent must be a constant scalar expression, got node kind '${node.kind}'.`);
104
79
  }
105
80
  /**
106
- * Differentiate a scalar bridge RHS AST with respect to one symbol via
107
- * reverse-mode AD. Every symbol other than `varName` is resolved to a constant
108
- * (caller `bindings`, then {@link NAMED_CONSTANTS}, then numeric literal); the
109
- * `bindings` must contain `varName` (its value sets the point of evaluation).
81
+ * Refuse the differentiation variable in any `^` exponent, and check that every
82
+ * exponent is a constant scalar expression. MathTS would differentiate a
83
+ * variable exponent; UPT keeps the constant-power contract.
84
+ */
85
+ function checkExponents(node, varName, bindings) {
86
+ if (node.kind === 'op') {
87
+ if (node.op === '^' && node.args.length === 2)
88
+ resolveExponent(node.args[1], varName, bindings);
89
+ for (const arg of node.args)
90
+ checkExponents(arg, varName, bindings);
91
+ }
92
+ else if (node.kind === 'transcendental' || node.kind === 'abs') {
93
+ checkExponents(node.arg, varName, bindings);
94
+ }
95
+ }
96
+ /**
97
+ * Differentiate a scalar bridge RHS AST with respect to one symbol through
98
+ * MathTS `compileScalarGradient`. Every symbol other than `varName` is resolved
99
+ * to a constant (caller `bindings`, then {@link NAMED_CONSTANTS}, then numeric
100
+ * literal); the `bindings` must contain `varName` (its value sets the point of
101
+ * evaluation).
110
102
  *
103
+ * @throws TypeError when `varName` is not bound, a symbol cannot be resolved,
104
+ * `varName` appears in an exponent, or a node kind is outside the scalar grammar.
111
105
  * @public
112
106
  */
113
- export async function bridgeGradientAST(rhs, varName, bindings) {
114
- if (!Object.prototype.hasOwnProperty.call(bindings, varName)) {
107
+ export function bridgeGradientAST(rhs, varName, bindings) {
108
+ if (!Object.hasOwn(bindings, varName)) {
115
109
  throw new TypeError(`bridgeGradientAST: bindings must contain the differentiation variable '${varName}'.`);
116
110
  }
117
- let autograd;
118
- let tensorMod;
111
+ checkExponents(rhs, varName, bindings);
112
+ let lowered;
119
113
  try {
120
- // The two modules are typed by the structural views above (the members
121
- // this lowering calls: reverseGrad, TapedTensor, Tensor.fromNested …).
122
- // The casts state that those members exist on the imported modules, which
123
- // their own type declarations guarantee; the full MathTS types are not
124
- // imported so this file compiles without the autograd types at hand.
125
- autograd =
126
- (await import('@danielsimonjr/mathts-autograd'));
127
- tensorMod =
128
- (await import('@danielsimonjr/mathts-tensor'));
114
+ lowered = toScalarNode(rhs);
129
115
  }
130
- catch {
131
- throw new EngineCapabilityError('mathts-autograd', 'reverseGrad');
116
+ catch (err) {
117
+ if (!(err instanceof ScalarBuildError))
118
+ throw err;
119
+ throw new TypeError(`bridgeGradientAST: unsupported node kind — ${err.message}. The differentiable grammar is ` +
120
+ `symbol + op(+ − * / ^) + transcendental + abs. Other encodings (tensor/curvature, integrals, ` +
121
+ `or physics hidden in a typed-stub symbol) are not exactly differentiable.`);
132
122
  }
133
- const { reverseGrad, TapedTensor } = autograd;
134
- const { Tensor } = tensorMod;
135
- // Build the (traced var) → scalar function reverseGrad expects. `x` arrives as
136
- // a TapedTensor; constants become extra tape leaves (their gradients are
137
- // computed and ignored — only `x`'s gradient is returned).
138
- const fn = (x) => {
139
- const constLeaf = (v) => TapedTensor.fromTensorAsInput(Tensor.fromNested(v, []), x.tape);
140
- // `boundVars` maps an integration variable name to its current (traced)
141
- // quadrature point; it is checked BEFORE the diff-var / constants so an
142
- // integration variable correctly shadows an outer symbol of the same name.
143
- const walk = (node, boundVars) => {
144
- if (node.kind === 'symbol') {
145
- const bound = boundVars.get(node.name);
146
- if (bound !== undefined)
147
- return bound;
148
- return node.name === varName
149
- ? x
150
- : constLeaf(resolveConstant(node.name, bindings));
151
- }
152
- if (node.kind === 'op') {
153
- switch (node.op) {
154
- case '+':
155
- return node.args.map((a) => walk(a, boundVars)).reduce((a, b) => a.add(b));
156
- case '-': {
157
- const terms = node.args.map((a) => walk(a, boundVars));
158
- // Unary minus (0 − t); n-ary subtract folds left.
159
- return terms.length === 1
160
- ? constLeaf(0).sub(terms[0])
161
- : terms.reduce((a, b) => a.sub(b));
162
- }
163
- case '*':
164
- return node.args.map((a) => walk(a, boundVars)).reduce((a, b) => a.mul(b));
165
- case '/':
166
- return node.args.map((a) => walk(a, boundVars)).reduce((a, b) => a.divide(b));
167
- case '^':
168
- return walk(node.args[0], boundVars).pow(resolveExponent(node.args[1], varName, bindings));
169
- default:
170
- throw new TypeError(`bridgeGradientAST: unsupported operator '${node.op}'.`);
171
- }
172
- }
173
- if (node.kind === 'transcendental') {
174
- return applyTranscendental(node.fn, walk(node.arg, boundVars));
175
- }
176
- if (node.kind === 'abs') {
177
- return walk(node.arg, boundVars).abs();
178
- }
179
- if (node.kind === 'integral') {
180
- // Definite integral via 16-point Gauss–Legendre quadrature, built from
181
- // traced ops so reverse-mode AD yields the gradient w.r.t. integrand
182
- // parameters (Leibniz: ∫ ∂f/∂θ) and w.r.t. bounds (∂Q/∂b ≈ f(b)).
183
- // NOTE: the gradient is EXACT for the quadrature, which APPROXIMATES the
184
- // true integral for non-polynomial integrands (exact for constant /
185
- // polynomial-degree ≤ 31 integrands).
186
- if (!node.lower || !node.upper) {
187
- throw new TypeError(`bridgeGradientAST: an abstract (bound-less) integral is not differentiable; ` +
188
- `supply lower/upper bounds for a definite integral.`);
189
- }
190
- if (node.over.kind !== 'symbol') {
191
- throw new TypeError(`bridgeGradientAST: integral 'over' must be a symbol.`);
192
- }
193
- const overName = node.over.name;
194
- if (overName === varName) {
195
- throw new TypeError(`bridgeGradientAST: cannot differentiate w.r.t. the integration variable '${overName}'.`);
196
- }
197
- const lo = walk(node.lower, boundVars);
198
- const hi = walk(node.upper, boundVars);
199
- const half = hi.sub(lo).scale(0.5); // (b − a)/2
200
- const mid = lo.add(hi).scale(0.5); // (a + b)/2
201
- let sum = null;
202
- for (const gl of GAUSS_LEGENDRE_16) {
203
- const xPoint = mid.add(half.scale(gl.node)); // (b−a)/2·ξ + (a+b)/2
204
- const env = new Map(boundVars);
205
- env.set(overName, xPoint);
206
- const term = walk(node.integrand, env).scale(gl.weight);
207
- sum = sum === null ? term : sum.add(term);
208
- }
209
- // GAUSS_LEGENDRE_16 is non-empty, so `sum` is set.
210
- return half.mul(sum);
211
- }
212
- throw new TypeError(`bridgeGradientAST: unsupported node kind '${node.kind}' — the differentiable ` +
213
- `grammar is symbol + op(+ − * / ^) + transcendental + abs + definite integral. ` +
214
- `Other encodings (tensor/curvature, bound-less integrals, or physics hidden in ` +
215
- `a typed-stub symbol) are not exactly differentiable.`);
216
- };
217
- return walk(rhs, new Map());
218
- };
219
- const xLeaf = Tensor.fromNested(bindings[varName], []);
220
- const { value, gradient } = reverseGrad(fn, xLeaf);
221
- return {
222
- value: value.toNested(),
223
- gradient: gradient.toNested(),
224
- };
123
+ const constants = {};
124
+ forEachScalarLeaf(rhs, (leaf) => {
125
+ if (leaf.name === varName)
126
+ return;
127
+ // A literal spelling stays MathTS's literal unless the caller bound that name.
128
+ if (isDecimalLiteral(leaf.name) && !Object.hasOwn(bindings, leaf.name))
129
+ return;
130
+ constants[leaf.name] = resolveConstant(leaf.name, bindings);
131
+ }, { transcendental: true, abs: true });
132
+ const compiled = compileScalarGradient(lowered, { variables: [], parameters: [varName], constants });
133
+ const { values, grad } = compiled.evaluate([], [bindings[varName]]);
134
+ return { value: values[0], gradient: grad[0] };
225
135
  }
226
136
  /**
227
137
  * Convenience wrapper around {@link bridgeGradientAST} that looks the RHS AST up
@@ -229,7 +139,7 @@ export async function bridgeGradientAST(rhs, varName, bindings) {
229
139
  *
230
140
  * Throws if the id has no encoded RHS in {@link BRIDGE_RHS_BY_ID} (e.g. BE-51/52,
231
141
  * which are closed-form evaluators without an AST). If the bridge's RHS is not in
232
- * the differentiable scalar grammar (a tensor/integral/curvature encoding), the
142
+ * the differentiable scalar grammar (a tensor, integral or curvature encoding), the
233
143
  * delegated `bridgeGradientAST` throws the "unsupported node kind" error — use
234
144
  * {@link astDifferentiableBridgeIds} to discover which ids are AD-eligible.
235
145
  *
@@ -244,7 +154,7 @@ export function bridgeGradientASTById(bridgeId, varName, bindings) {
244
154
  }
245
155
  return bridgeGradientAST(rhs, varName, bindings);
246
156
  }
247
- /** Whether an AST consists solely of the differentiable scalar grammar (symbol + op(+ − * / ^)). */
157
+ /** Whether an AST consists solely of the differentiable scalar grammar (symbol + op(+ − * / ^) + transcendental + abs). */
248
158
  function isDifferentiableScalarAST(node) {
249
159
  if (node.kind === 'symbol')
250
160
  return true;
@@ -253,24 +163,16 @@ function isDifferentiableScalarAST(node) {
253
163
  return false;
254
164
  return node.args.every(isDifferentiableScalarAST);
255
165
  }
256
- if (node.kind === 'transcendental')
166
+ if (node.kind === 'transcendental' || node.kind === 'abs')
257
167
  return isDifferentiableScalarAST(node.arg);
258
- if (node.kind === 'abs')
259
- return isDifferentiableScalarAST(node.arg);
260
- if (node.kind === 'integral') {
261
- return (!!node.lower &&
262
- !!node.upper &&
263
- node.over.kind === 'symbol' &&
264
- isDifferentiableScalarAST(node.integrand) &&
265
- isDifferentiableScalarAST(node.lower) &&
266
- isDifferentiableScalarAST(node.upper));
267
- }
268
168
  return false;
269
169
  }
270
170
  /**
271
171
  * The catalog bridge ids whose encoded RHS lies entirely in the differentiable
272
- * scalar grammar — i.e. those `bridgeGradientASTById` can differentiate exactly.
273
- * Bridges encoded with tensor/integral/curvature nodes (or whose physics is
172
+ * scalar grammar. `bridgeGradientASTById` differentiates each of them exactly
173
+ * with respect to any variable that is not in an exponent; a variable in an
174
+ * exponent is refused (`checkExponents`).
175
+ * Bridges encoded with tensor, integral or curvature nodes (or whose physics is
274
176
  * hidden behind a typed-stub symbol) are excluded from EXACT differentiation
275
177
  * (a stub still differentiates w.r.t. the stub symbol, but not the physics
276
178
  * inside it). Returned sorted ascending.
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-ast-gradient.js","sourceRoot":"","sources":["../../src/diff/bridge-ast-gradient.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Exact bridge-gradients via reverse-mode AD over the symbolic RHS AST.\n *\n * The catalog's plain-JS evaluators cannot be differentiated by AD (they run\n * raw `Math.*` arithmetic and return a number, which carries no tape; see\n * `bridge-gradient.ts`). But each bridge also carries a faithful symbolic\n * encoding of its RHS (`*_RHS: ExprNode`). This module lowers that scalar AST\n * through `@danielsimonjr/mathts-autograd`'s `TapedTensor` ops — binding the\n * chosen variable to a traced input and every other symbol to a constant tape\n * leaf — so `reverseGrad` returns the EXACT derivative (machine precision), with\n * the plain-JS evaluators left untouched (P8 Decision #1).\n *\n * Scope: the dimensional scalar grammar is `symbol` + `op(+ − * / ^)` (the\n * transcendentals of a bridge are absorbed into typed-stub *symbols*, so they\n * appear as opaque constants here). A bridge encoded with a typed stub therefore\n * differentiates with respect to the stub, not the physics inside it — faithful,\n * fully-expanded encodings (e.g. BE-42 `ℏc³/(8πGM·k_B)`) differentiate exactly.\n * `^` exponents must be constant (the differentiation variable may not appear in\n * an exponent).\n *\n * Loads `@danielsimonjr/mathts-autograd` at the call and throws\n * `EngineCapabilityError` when that import fails.\n *\n * @module diff/bridge-ast-gradient\n */\n\nimport type { ExprNode, TranscendentalFn } from '../dimensional/validator.js';\nimport { constantScope } from '../dimensional/symbolic-constants.js';\nimport { EngineCapabilityError } from '../numerical/errors.js';\nimport { BRIDGE_RHS_BY_ID, parseBridgeId } from '../bridges/rhs-registry.js';\nimport { GAUSS_LEGENDRE_16 } from '../numerical/quadrature.js';\n\n/**\n * Result of {@link bridgeGradientAST}: the bridge's scalar `value` at the\n * supplied point and the exact `gradient` d(value)/d(varName).\n *\n * @public\n */\nexport interface ASTGradientResult {\n readonly value: number;\n readonly gradient: number;\n}\n\n// --- Structural views of the autograd/tensor packages (required dependencies; the\n// dynamic import keeps the AD path off the module's load, and its result is narrowed\n// to the call-site shape). ---\n\ntype TapeHandle = unknown;\n\ninterface TapedScalar {\n readonly tape: TapeHandle;\n add(o: TapedScalar): TapedScalar;\n sub(o: TapedScalar): TapedScalar;\n mul(o: TapedScalar): TapedScalar;\n divide(o: TapedScalar): TapedScalar;\n scale(k: number): TapedScalar;\n pow(k: number): TapedScalar;\n exp(): TapedScalar;\n log(): TapedScalar;\n log2(): TapedScalar;\n log10(): TapedScalar;\n sin(): TapedScalar;\n cos(): TapedScalar;\n tan(): TapedScalar;\n sinh(): TapedScalar;\n cosh(): TapedScalar;\n tanh(): TapedScalar;\n abs(): TapedScalar;\n}\n\n/** Map a transcendental fn name to the TapedTensor method that traces it. */\nfunction applyTranscendental(fn: TranscendentalFn, t: TapedScalar): TapedScalar {\n switch (fn) {\n case 'exp':\n return t.exp();\n case 'ln':\n return t.log();\n case 'log2':\n return t.log2();\n case 'log10':\n return t.log10();\n case 'sin':\n return t.sin();\n case 'cos':\n return t.cos();\n case 'tan':\n return t.tan();\n case 'sinh':\n return t.sinh();\n case 'cosh':\n return t.cosh();\n case 'tanh':\n return t.tanh();\n }\n}\n\ninterface MathTSTensorLike {\n toNested(): unknown;\n}\n\ninterface AutogradModuleLike {\n reverseGrad(\n fn: (x: TapedScalar) => TapedScalar,\n x: MathTSTensorLike,\n cotangent?: MathTSTensorLike,\n ): { value: MathTSTensorLike; gradient: MathTSTensorLike };\n TapedTensor: {\n fromTensorAsInput(t: MathTSTensorLike, tape: TapeHandle): TapedScalar;\n };\n}\n\ninterface TensorModuleLike {\n Tensor: { fromNested(data: number, shape: number[]): MathTSTensorLike };\n}\n\n/**\n * Named physical constants the bridge encodings reference as bare symbols:\n * every registered spelling, from the constant registry, plus π and its\n * registered multiples, so a traced AST evaluates to the SAME number as the\n * numeric evaluator. Callers can override or extend via the `bindings` argument.\n */\nconst NAMED_CONSTANTS: Readonly<Record<string, number>> = constantScope();\n\n/** Resolve a symbol's numeric value: caller bindings → named constants → numeric literal. */\nfunction resolveConstant(\n name: string,\n bindings: Record<string, number>,\n): number {\n if (Object.prototype.hasOwnProperty.call(bindings, name))\n return bindings[name];\n if (Object.prototype.hasOwnProperty.call(NAMED_CONSTANTS, name))\n return NAMED_CONSTANTS[name];\n const asNumber = Number(name);\n if (name.trim() !== '' && Number.isFinite(asNumber)) return asNumber;\n throw new TypeError(\n `bridgeGradientAST: unknown symbol '${name}' — not a binding, a named constant ` +\n `(${Object.keys(NAMED_CONSTANTS).join(', ')}), or a numeric literal. Supply it in bindings.`,\n );\n}\n\n/** Resolve a `^` exponent to a numeric constant expression.\n *\n * The exponent may contain scalar arithmetic over constants/bindings (for\n * example `-1/z`) as long as it is independent of the differentiation\n * variable. Variable exponents require a traced `pow(base, exponent)` op,\n * which the current MathTS tape surface does not expose, so they are rejected\n * explicitly rather than being silently detached from AD.\n */\nfunction resolveExponent(\n node: ExprNode,\n varName: string,\n bindings: Record<string, number>,\n): number {\n if (node.kind === 'symbol') {\n if (node.name === varName) {\n throw new TypeError(\n `bridgeGradientAST: differentiation variable '${varName}' appears as an exponent — ` +\n `variable exponents are not supported (only constant powers).`,\n );\n }\n return resolveConstant(node.name, bindings);\n }\n if (node.kind === 'op') {\n const values = node.args.map((arg) => resolveExponent(arg, varName, bindings));\n if (values.length === 0) {\n throw new TypeError(`bridgeGradientAST: '${node.op}' exponent expression has no operands.`);\n }\n switch (node.op) {\n case '+': return values.reduce((a, b) => a + b);\n case '-': return values.length === 1 ? -values[0] : values.slice(1).reduce((a, b) => a - b, values[0]);\n case '*': return values.reduce((a, b) => a * b);\n case '/': return values.slice(1).reduce((a, b) => a / b, values[0]);\n case '^': return values.slice(1).reduce((a, b) => a ** b, values[0]);\n }\n }\n throw new TypeError(\n `bridgeGradientAST: '^' exponent must be a constant scalar expression, got node kind '${node.kind}'.`,\n );\n}\n\n/**\n * Differentiate a scalar bridge RHS AST with respect to one symbol via\n * reverse-mode AD. Every symbol other than `varName` is resolved to a constant\n * (caller `bindings`, then {@link NAMED_CONSTANTS}, then numeric literal); the\n * `bindings` must contain `varName` (its value sets the point of evaluation).\n *\n * @public\n */\nexport async function bridgeGradientAST(\n rhs: ExprNode,\n varName: string,\n bindings: Record<string, number>,\n): Promise<ASTGradientResult> {\n if (!Object.prototype.hasOwnProperty.call(bindings, varName)) {\n throw new TypeError(\n `bridgeGradientAST: bindings must contain the differentiation variable '${varName}'.`,\n );\n }\n\n let autograd: AutogradModuleLike;\n let tensorMod: TensorModuleLike;\n try {\n // The two modules are typed by the structural views above (the members\n // this lowering calls: reverseGrad, TapedTensor, Tensor.fromNested …).\n // The casts state that those members exist on the imported modules, which\n // their own type declarations guarantee; the full MathTS types are not\n // imported so this file compiles without the autograd types at hand.\n autograd =\n (await import('@danielsimonjr/mathts-autograd')) as unknown as AutogradModuleLike;\n tensorMod =\n (await import('@danielsimonjr/mathts-tensor')) as unknown as TensorModuleLike;\n } catch {\n throw new EngineCapabilityError('mathts-autograd', 'reverseGrad');\n }\n const { reverseGrad, TapedTensor } = autograd;\n const { Tensor } = tensorMod;\n\n // Build the (traced var) → scalar function reverseGrad expects. `x` arrives as\n // a TapedTensor; constants become extra tape leaves (their gradients are\n // computed and ignored — only `x`'s gradient is returned).\n const fn = (x: TapedScalar): TapedScalar => {\n const constLeaf = (v: number): TapedScalar =>\n TapedTensor.fromTensorAsInput(Tensor.fromNested(v, []), x.tape);\n\n // `boundVars` maps an integration variable name to its current (traced)\n // quadrature point; it is checked BEFORE the diff-var / constants so an\n // integration variable correctly shadows an outer symbol of the same name.\n const walk = (node: ExprNode, boundVars: ReadonlyMap<string, TapedScalar>): TapedScalar => {\n if (node.kind === 'symbol') {\n const bound = boundVars.get(node.name);\n if (bound !== undefined) return bound;\n return node.name === varName\n ? x\n : constLeaf(resolveConstant(node.name, bindings));\n }\n if (node.kind === 'op') {\n switch (node.op) {\n case '+':\n return node.args.map((a) => walk(a, boundVars)).reduce((a, b) => a.add(b));\n case '-': {\n const terms = node.args.map((a) => walk(a, boundVars));\n // Unary minus (0 − t); n-ary subtract folds left.\n return terms.length === 1\n ? constLeaf(0).sub(terms[0])\n : terms.reduce((a, b) => a.sub(b));\n }\n case '*':\n return node.args.map((a) => walk(a, boundVars)).reduce((a, b) => a.mul(b));\n case '/':\n return node.args.map((a) => walk(a, boundVars)).reduce((a, b) => a.divide(b));\n case '^':\n return walk(node.args[0], boundVars).pow(\n resolveExponent(node.args[1], varName, bindings),\n );\n default:\n throw new TypeError(\n `bridgeGradientAST: unsupported operator '${node.op}'.`,\n );\n }\n }\n if (node.kind === 'transcendental') {\n return applyTranscendental(node.fn, walk(node.arg, boundVars));\n }\n if (node.kind === 'abs') {\n return walk(node.arg, boundVars).abs();\n }\n if (node.kind === 'integral') {\n // Definite integral via 16-point Gauss–Legendre quadrature, built from\n // traced ops so reverse-mode AD yields the gradient w.r.t. integrand\n // parameters (Leibniz: ∫ ∂f/∂θ) and w.r.t. bounds (∂Q/∂b ≈ f(b)).\n // NOTE: the gradient is EXACT for the quadrature, which APPROXIMATES the\n // true integral for non-polynomial integrands (exact for constant /\n // polynomial-degree ≤ 31 integrands).\n if (!node.lower || !node.upper) {\n throw new TypeError(\n `bridgeGradientAST: an abstract (bound-less) integral is not differentiable; ` +\n `supply lower/upper bounds for a definite integral.`,\n );\n }\n if (node.over.kind !== 'symbol') {\n throw new TypeError(`bridgeGradientAST: integral 'over' must be a symbol.`);\n }\n const overName = node.over.name;\n if (overName === varName) {\n throw new TypeError(\n `bridgeGradientAST: cannot differentiate w.r.t. the integration variable '${overName}'.`,\n );\n }\n const lo = walk(node.lower, boundVars);\n const hi = walk(node.upper, boundVars);\n const half = hi.sub(lo).scale(0.5); // (b − a)/2\n const mid = lo.add(hi).scale(0.5); // (a + b)/2\n let sum: TapedScalar | null = null;\n for (const gl of GAUSS_LEGENDRE_16) {\n const xPoint = mid.add(half.scale(gl.node)); // (b−a)/2·ξ + (a+b)/2\n const env = new Map(boundVars);\n env.set(overName, xPoint);\n const term = walk(node.integrand, env).scale(gl.weight);\n sum = sum === null ? term : sum.add(term);\n }\n // GAUSS_LEGENDRE_16 is non-empty, so `sum` is set.\n return half.mul(sum as TapedScalar);\n }\n throw new TypeError(\n `bridgeGradientAST: unsupported node kind '${node.kind}' — the differentiable ` +\n `grammar is symbol + op(+ − * / ^) + transcendental + abs + definite integral. ` +\n `Other encodings (tensor/curvature, bound-less integrals, or physics hidden in ` +\n `a typed-stub symbol) are not exactly differentiable.`,\n );\n };\n\n return walk(rhs, new Map());\n };\n\n const xLeaf = Tensor.fromNested(bindings[varName], []);\n const { value, gradient } = reverseGrad(fn, xLeaf);\n return {\n value: value.toNested() as number,\n gradient: gradient.toNested() as number,\n };\n}\n\n/**\n * Convenience wrapper around {@link bridgeGradientAST} that looks the RHS AST up\n * by catalog bridge id. `bridgeId` accepts a number, `'42'`, or `'BE-42'`.\n *\n * Throws if the id has no encoded RHS in {@link BRIDGE_RHS_BY_ID} (e.g. BE-51/52,\n * which are closed-form evaluators without an AST). If the bridge's RHS is not in\n * the differentiable scalar grammar (a tensor/integral/curvature encoding), the\n * delegated `bridgeGradientAST` throws the \"unsupported node kind\" error — use\n * {@link astDifferentiableBridgeIds} to discover which ids are AD-eligible.\n *\n * @public\n */\nexport function bridgeGradientASTById(\n bridgeId: number | string,\n varName: string,\n bindings: Record<string, number>\n): Promise<ASTGradientResult> {\n const id = parseBridgeId(bridgeId);\n const rhs = BRIDGE_RHS_BY_ID.get(id);\n if (rhs === undefined) {\n throw new TypeError(\n `bridgeGradientASTById: BE-${id} has no encoded RHS AST in the registry ` +\n `(BE-51/52 are closed-form; ${BRIDGE_RHS_BY_ID.size} bridges have an encoded AST).`\n );\n }\n return bridgeGradientAST(rhs, varName, bindings);\n}\n\n/** Whether an AST consists solely of the differentiable scalar grammar (symbol + op(+ − * / ^)). */\nfunction isDifferentiableScalarAST(node: ExprNode): boolean {\n if (node.kind === 'symbol') return true;\n if (node.kind === 'op') {\n if (!['+', '-', '*', '/', '^'].includes(node.op)) return false;\n return node.args.every(isDifferentiableScalarAST);\n }\n if (node.kind === 'transcendental') return isDifferentiableScalarAST(node.arg);\n if (node.kind === 'abs') return isDifferentiableScalarAST(node.arg);\n if (node.kind === 'integral') {\n return (\n !!node.lower &&\n !!node.upper &&\n node.over.kind === 'symbol' &&\n isDifferentiableScalarAST(node.integrand) &&\n isDifferentiableScalarAST(node.lower) &&\n isDifferentiableScalarAST(node.upper)\n );\n }\n return false;\n}\n\n/**\n * The catalog bridge ids whose encoded RHS lies entirely in the differentiable\n * scalar grammar — i.e. those `bridgeGradientASTById` can differentiate exactly.\n * Bridges encoded with tensor/integral/curvature nodes (or whose physics is\n * hidden behind a typed-stub symbol) are excluded from EXACT differentiation\n * (a stub still differentiates w.r.t. the stub symbol, but not the physics\n * inside it). Returned sorted ascending.\n *\n * @public\n */\nexport function astDifferentiableBridgeIds(): number[] {\n const ids: number[] = [];\n for (const [id, rhs] of BRIDGE_RHS_BY_ID) {\n if (isDifferentiableScalarAST(rhs)) ids.push(id);\n }\n return ids.sort((a, b) => a - b);\n}\n"]}
1
+ {"version":3,"file":"bridge-ast-gradient.js","sourceRoot":"","sources":["../../src/diff/bridge-ast-gradient.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Exact bridge-gradients over the symbolic RHS AST.\n *\n * The catalog's plain-JS evaluators cannot be differentiated by AD (they run\n * raw `Math.*` arithmetic and return a number, which carries no tape; see\n * `bridge-gradient.ts`). But each bridge also carries a faithful symbolic\n * encoding of its RHS (`*_RHS: ExprNode`). This module lowers that scalar AST\n * with the MathTS scalar builder and differentiates it with MathTS\n * `compileScalarGradient`: the chosen variable is the one parameter, and every\n * other symbol is a constant. The result is the EXACT derivative (machine\n * precision); the plain-JS evaluators stay untouched (P8 Decision #1).\n *\n * Scope: the scalar grammar is `symbol` + `op(+ − * / ^)` + `transcendental` +\n * `abs`. A bridge encoded with a typed stub differentiates with respect to the\n * stub, not the physics inside it; fully-expanded encodings (e.g. BE-42\n * `ℏc³/(8πGM·k_B)`) differentiate exactly. A `^` exponent must be constant: a\n * UPT pre-check refuses the differentiation variable in an exponent before\n * MathTS sees the tree.\n *\n * A point where the value overflows gives `NaN` for the value and the\n * gradient (MathTS marks the row invalid), not `±Infinity`.\n *\n * @module diff/bridge-ast-gradient\n */\n\nimport { ScalarBuildError } from '@danielsimonjr/mathts-expression';\nimport { compileScalarGradient } from '@danielsimonjr/mathts-functions';\nimport type { ExprNode } from '../dimensional/validator.js';\nimport { forEachScalarLeaf, isDecimalLiteral } from '../dimensional/ast-leaves.js';\nimport { constantScope } from '../dimensional/symbolic-constants.js';\nimport { toScalarNode } from '../dimensional/scalar-lowering.js';\nimport { BRIDGE_RHS_BY_ID, parseBridgeId } from '../bridges/rhs-registry.js';\n\n/**\n * Result of {@link bridgeGradientAST}: the bridge's scalar `value` at the\n * supplied point and the exact `gradient` d(value)/d(varName).\n *\n * @public\n */\nexport interface ASTGradientResult {\n readonly value: number;\n readonly gradient: number;\n}\n\n/**\n * Named physical constants the bridge encodings reference as bare symbols:\n * every registered spelling, from the constant registry, plus π and its\n * registered multiples, so the AST evaluates to the SAME number as the\n * numeric evaluator. Callers can override or extend via the `bindings` argument.\n */\nconst NAMED_CONSTANTS: Readonly<Record<string, number>> = constantScope();\n\n/** Resolve a symbol's numeric value: caller bindings → named constants → numeric literal. */\nfunction resolveConstant(\n name: string,\n bindings: Record<string, number>,\n): number {\n if (Object.hasOwn(bindings, name))\n return bindings[name];\n if (Object.hasOwn(NAMED_CONSTANTS, name))\n return NAMED_CONSTANTS[name];\n const asNumber = Number(name);\n if (name.trim() !== '' && Number.isFinite(asNumber)) return asNumber;\n throw new TypeError(\n `bridgeGradientAST: unknown symbol '${name}' — not a binding, a named constant ` +\n `(${Object.keys(NAMED_CONSTANTS).join(', ')}), or a numeric literal. Supply it in bindings.`,\n );\n}\n\n/** Resolve a `^` exponent to a numeric constant expression.\n *\n * The exponent may contain scalar arithmetic over constants/bindings (for\n * example `-1/z`) as long as it is independent of the differentiation\n * variable. UPT keeps the constant-power contract, so the differentiation\n * variable in an exponent is refused before MathTS reads the tree.\n */\nfunction resolveExponent(\n node: ExprNode,\n varName: string,\n bindings: Record<string, number>,\n): number {\n if (node.kind === 'symbol') {\n if (node.name === varName) {\n throw new TypeError(\n `bridgeGradientAST: differentiation variable '${varName}' appears as an exponent — ` +\n `variable exponents are not supported (only constant powers).`,\n );\n }\n return resolveConstant(node.name, bindings);\n }\n if (node.kind === 'op') {\n const values = node.args.map((arg) => resolveExponent(arg, varName, bindings));\n if (values.length === 0) {\n throw new TypeError(`bridgeGradientAST: '${node.op}' exponent expression has no operands.`);\n }\n switch (node.op) {\n case '+': return values.reduce((a, b) => a + b);\n case '-': return values.length === 1 ? -values[0] : values.slice(1).reduce((a, b) => a - b, values[0]);\n case '*': return values.reduce((a, b) => a * b);\n case '/': return values.slice(1).reduce((a, b) => a / b, values[0]);\n case '^': return values.slice(1).reduce((a, b) => a ** b, values[0]);\n }\n }\n throw new TypeError(\n `bridgeGradientAST: '^' exponent must be a constant scalar expression, got node kind '${node.kind}'.`,\n );\n}\n\n/**\n * Refuse the differentiation variable in any `^` exponent, and check that every\n * exponent is a constant scalar expression. MathTS would differentiate a\n * variable exponent; UPT keeps the constant-power contract.\n */\nfunction checkExponents(node: ExprNode, varName: string, bindings: Record<string, number>): void {\n if (node.kind === 'op') {\n if (node.op === '^' && node.args.length === 2) resolveExponent(node.args[1], varName, bindings);\n for (const arg of node.args) checkExponents(arg, varName, bindings);\n } else if (node.kind === 'transcendental' || node.kind === 'abs') {\n checkExponents(node.arg, varName, bindings);\n }\n}\n\n/**\n * Differentiate a scalar bridge RHS AST with respect to one symbol through\n * MathTS `compileScalarGradient`. Every symbol other than `varName` is resolved\n * to a constant (caller `bindings`, then {@link NAMED_CONSTANTS}, then numeric\n * literal); the `bindings` must contain `varName` (its value sets the point of\n * evaluation).\n *\n * @throws TypeError when `varName` is not bound, a symbol cannot be resolved,\n * `varName` appears in an exponent, or a node kind is outside the scalar grammar.\n * @public\n */\nexport function bridgeGradientAST(\n rhs: ExprNode,\n varName: string,\n bindings: Record<string, number>,\n): ASTGradientResult {\n if (!Object.hasOwn(bindings, varName)) {\n throw new TypeError(\n `bridgeGradientAST: bindings must contain the differentiation variable '${varName}'.`,\n );\n }\n checkExponents(rhs, varName, bindings);\n\n let lowered: ReturnType<typeof toScalarNode>;\n try {\n lowered = toScalarNode(rhs);\n } catch (err) {\n if (!(err instanceof ScalarBuildError)) throw err;\n throw new TypeError(\n `bridgeGradientAST: unsupported node kind — ${err.message}. The differentiable grammar is ` +\n `symbol + op(+ − * / ^) + transcendental + abs. Other encodings (tensor/curvature, integrals, ` +\n `or physics hidden in a typed-stub symbol) are not exactly differentiable.`,\n ); }\n\n const constants: Record<string, number> = {};\n forEachScalarLeaf(\n rhs,\n (leaf) => {\n if (leaf.name === varName) return;\n // A literal spelling stays MathTS's literal unless the caller bound that name.\n if (isDecimalLiteral(leaf.name) && !Object.hasOwn(bindings, leaf.name)) return;\n constants[leaf.name] = resolveConstant(leaf.name, bindings);\n },\n { transcendental: true, abs: true },\n );\n\n const compiled = compileScalarGradient(lowered, { variables: [], parameters: [varName], constants });\n const { values, grad } = compiled.evaluate([], [bindings[varName]]);\n return { value: values[0]!, gradient: grad[0]! };\n}\n\n/**\n * Convenience wrapper around {@link bridgeGradientAST} that looks the RHS AST up\n * by catalog bridge id. `bridgeId` accepts a number, `'42'`, or `'BE-42'`.\n *\n * Throws if the id has no encoded RHS in {@link BRIDGE_RHS_BY_ID} (e.g. BE-51/52,\n * which are closed-form evaluators without an AST). If the bridge's RHS is not in\n * the differentiable scalar grammar (a tensor, integral or curvature encoding), the\n * delegated `bridgeGradientAST` throws the \"unsupported node kind\" error — use\n * {@link astDifferentiableBridgeIds} to discover which ids are AD-eligible.\n *\n * @public\n */\nexport function bridgeGradientASTById(\n bridgeId: number | string,\n varName: string,\n bindings: Record<string, number>\n): ASTGradientResult {\n const id = parseBridgeId(bridgeId);\n const rhs = BRIDGE_RHS_BY_ID.get(id);\n if (rhs === undefined) {\n throw new TypeError(\n `bridgeGradientASTById: BE-${id} has no encoded RHS AST in the registry ` +\n `(BE-51/52 are closed-form; ${BRIDGE_RHS_BY_ID.size} bridges have an encoded AST).`\n );\n }\n return bridgeGradientAST(rhs, varName, bindings);\n}\n\n/** Whether an AST consists solely of the differentiable scalar grammar (symbol + op(+ − * / ^) + transcendental + abs). */\nfunction isDifferentiableScalarAST(node: ExprNode): boolean {\n if (node.kind === 'symbol') return true;\n if (node.kind === 'op') {\n if (!['+', '-', '*', '/', '^'].includes(node.op)) return false;\n return node.args.every(isDifferentiableScalarAST);\n }\n if (node.kind === 'transcendental' || node.kind === 'abs') return isDifferentiableScalarAST(node.arg);\n return false;\n}\n\n/**\n * The catalog bridge ids whose encoded RHS lies entirely in the differentiable\n * scalar grammar. `bridgeGradientASTById` differentiates each of them exactly\n * with respect to any variable that is not in an exponent; a variable in an\n * exponent is refused (`checkExponents`).\n * Bridges encoded with tensor, integral or curvature nodes (or whose physics is\n * hidden behind a typed-stub symbol) are excluded from EXACT differentiation\n * (a stub still differentiates w.r.t. the stub symbol, but not the physics\n * inside it). Returned sorted ascending.\n *\n * @public\n */\nexport function astDifferentiableBridgeIds(): number[] {\n const ids: number[] = [];\n for (const [id, rhs] of BRIDGE_RHS_BY_ID) {\n if (isDifferentiableScalarAST(rhs)) ids.push(id);\n }\n return ids.sort((a, b) => a - b);\n}\n"]}
@@ -18,8 +18,11 @@
18
18
  */
19
19
  import type { Dimension } from './types.js';
20
20
  import type { CurvatureCompositeNode } from './curvature-composite.js';
21
+ /** The position of a tensor index: upper (contravariant) or lower (covariant). */
21
22
  export type Variance = 'upper' | 'lower';
23
+ /** The semantic class of a tensor symbol: coordinate, field or constant. */
22
24
  export type Role = 'coordinate' | 'field' | 'constant';
25
+ /** One index of a tensor: its label and whether it is upper or lower. */
23
26
  export interface TensorIndex {
24
27
  readonly label: string;
25
28
  readonly variance: Variance;
@@ -40,6 +43,7 @@ export interface UpperIndex {
40
43
  * are excluded — they produce fractional dimensions.)
41
44
  */
42
45
  export type TranscendentalFn = 'exp' | 'ln' | 'log2' | 'log10' | 'sin' | 'cos' | 'tan' | 'sinh' | 'cosh' | 'tanh';
46
+ /** A named tensor, with its indices, dimension and optional role. */
43
47
  export interface TensorSymbolNode {
44
48
  readonly kind: 'tensor-symbol';
45
49
  readonly name: string;
@@ -51,10 +55,12 @@ export interface TensorSymbolNode {
51
55
  * operand (default 'symbolic'). */
52
56
  readonly numericalForm?: 'symbolic' | 'numerical-fn' | 'grid';
53
57
  }
58
+ /** An Einstein-summation product of tensor and scalar operands. */
54
59
  export interface TensorProductNode {
55
60
  readonly kind: 'tensor-product';
56
61
  readonly args: ReadonlyArray<ExprNode>;
57
62
  }
63
+ /** A metric tensor, with its name, indices, signature and dimension. */
58
64
  export interface MetricTensorNode {
59
65
  readonly kind: 'metric-tensor';
60
66
  readonly name: string;
@@ -64,11 +70,13 @@ export interface MetricTensorNode {
64
70
  /** v0.4.0 numerical-lowering hint for ∂g (default 'computed'). */
65
71
  readonly derivativeStrategy?: 'computed' | 'zero' | 'supplied';
66
72
  }
73
+ /** The Kronecker delta, with its tensor indices and dimension. */
67
74
  export interface KroneckerDeltaNode {
68
75
  readonly kind: 'kronecker-delta';
69
76
  readonly indices: ReadonlyArray<TensorIndex>;
70
77
  readonly dim: Dimension;
71
78
  }
79
+ /** The partial derivative of an expression, taken on a covariant index. */
72
80
  export interface TensorPartialDerivativeNode {
73
81
  readonly kind: 'tensor-partial-derivative';
74
82
  readonly of: ExprNode;
@@ -88,6 +96,7 @@ export interface CovariantDerivativeNode {
88
96
  readonly gLower: MetricTensorNode;
89
97
  readonly gInverse: MetricTensorNode;
90
98
  }
99
+ /** The Riemann curvature tensor, with one upper and three lower indices. */
91
100
  export type RiemannTensorNode = CurvatureCompositeNode<'riemann-tensor', {
92
101
  readonly upperIndex: UpperIndex;
93
102
  readonly lowerIndices: readonly [CovariantIndex, CovariantIndex, CovariantIndex];
@@ -126,6 +135,7 @@ export type BianchiResidualNode = CurvatureCompositeNode<'bianchi-residual', {
126
135
  /** The Riemann tensor whose cyclic-derivative identity is checked. */
127
136
  readonly riemann: RiemannTensorNode;
128
137
  }>;
138
+ /** The Weyl curvature tensor, the trace-free part of the Riemann tensor. */
129
139
  export type WeylTensorNode = CurvatureCompositeNode<'weyl-tensor', {
130
140
  /** Metric g_{μν} (both-lower). Present for numerical lowering consumers. */
131
141
  readonly metric: MetricTensorNode;
@@ -147,11 +157,13 @@ export type KretschmannScalarNode = CurvatureCompositeNode<'kretschmann-scalar',
147
157
  /** Metric (both-lower preferred). */
148
158
  readonly metric: MetricTensorNode;
149
159
  }>;
160
+ /** A Killing vector field, with its vector and the metric it preserves. */
150
161
  export interface KillingVectorNode {
151
162
  readonly kind: 'killing-vector';
152
163
  readonly vector: TensorSymbolNode;
153
164
  readonly metric: MetricTensorNode;
154
165
  }
166
+ /** A conserved charge: a Killing vector contracted with a momentum. */
155
167
  export interface ConservedChargeNode {
156
168
  readonly kind: 'conserved-charge';
157
169
  /** The Killing vector ξ^μ (rank-1, upper variance). */
@@ -160,6 +172,7 @@ export interface ConservedChargeNode {
160
172
  * vector's index label so ξ^μ p_μ is well-formed. */
161
173
  readonly momentum: TensorSymbolNode;
162
174
  }
175
+ /** The symmetric stress-energy tensor T, with its indices and component dimension. */
163
176
  export interface StressEnergyTensorNode {
164
177
  readonly kind: 'stress-energy';
165
178
  readonly symbol: 'T';
@@ -167,6 +180,7 @@ export interface StressEnergyTensorNode {
167
180
  readonly symmetry: 'symmetric';
168
181
  readonly componentDim: Dimension;
169
182
  }
183
+ /** The cosmological constant Λ, with its dimension and optional numeric value. */
170
184
  export interface CosmologicalConstantNode {
171
185
  readonly kind: 'cosmological-constant';
172
186
  readonly symbol: 'Λ';
@@ -186,6 +200,7 @@ export interface EinsteinFieldEquationNode {
186
200
  readonly rhs: StressEnergyTensorNode;
187
201
  readonly coupling: 'einstein';
188
202
  }
203
+ /** Any node of a dimensional expression: scalar, calculus, tensor, curvature or field-equation. */
189
204
  export type ExprNode = {
190
205
  kind: 'symbol';
191
206
  name: string;