@graphty/layout 1.2.9 → 1.4.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 (237) hide show
  1. package/README.md +400 -409
  2. package/dist/layout.js +884 -817
  3. package/dist/layout.js.map +1 -1
  4. package/dist/src/algorithms/index.d.ts +3 -2
  5. package/dist/src/algorithms/index.d.ts.map +1 -0
  6. package/dist/src/algorithms/index.js +2 -2
  7. package/dist/src/algorithms/index.js.map +1 -1
  8. package/dist/src/algorithms/optimization/index.d.ts +5 -4
  9. package/dist/src/algorithms/optimization/index.d.ts.map +1 -0
  10. package/dist/src/algorithms/optimization/index.js +3 -3
  11. package/dist/src/algorithms/optimization/index.js.map +1 -1
  12. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +3 -5
  13. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts.map +1 -0
  14. package/dist/src/algorithms/optimization/kamada-kawai-solver.js +6 -9
  15. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -1
  16. package/dist/src/algorithms/optimization/lbfgs.d.ts +3 -3
  17. package/dist/src/algorithms/optimization/lbfgs.d.ts.map +1 -0
  18. package/dist/src/algorithms/optimization/lbfgs.js +5 -7
  19. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -1
  20. package/dist/src/algorithms/optimization/line-search.d.ts +1 -1
  21. package/dist/src/algorithms/optimization/line-search.d.ts.map +1 -0
  22. package/dist/src/algorithms/optimization/line-search.js +0 -1
  23. package/dist/src/algorithms/optimization/line-search.js.map +1 -1
  24. package/dist/src/algorithms/optimization/types.d.ts +2 -1
  25. package/dist/src/algorithms/optimization/types.d.ts.map +1 -0
  26. package/dist/src/algorithms/planarity/check.d.ts +5 -4
  27. package/dist/src/algorithms/planarity/check.d.ts.map +1 -0
  28. package/dist/src/algorithms/planarity/check.js +6 -6
  29. package/dist/src/algorithms/planarity/check.js.map +1 -1
  30. package/dist/src/algorithms/planarity/embedding.d.ts +5 -6
  31. package/dist/src/algorithms/planarity/embedding.d.ts.map +1 -0
  32. package/dist/src/algorithms/planarity/embedding.js +15 -14
  33. package/dist/src/algorithms/planarity/embedding.js.map +1 -1
  34. package/dist/src/algorithms/planarity/index.d.ts +5 -4
  35. package/dist/src/algorithms/planarity/index.d.ts.map +1 -0
  36. package/dist/src/algorithms/planarity/index.js +4 -4
  37. package/dist/src/algorithms/planarity/index.js.map +1 -1
  38. package/dist/src/algorithms/planarity/lr-test.d.ts +3 -2
  39. package/dist/src/algorithms/planarity/lr-test.d.ts.map +1 -0
  40. package/dist/src/algorithms/planarity/lr-test.js +2 -2
  41. package/dist/src/algorithms/planarity/lr-test.js.map +1 -1
  42. package/dist/src/algorithms/planarity/special-graphs.d.ts +2 -4
  43. package/dist/src/algorithms/planarity/special-graphs.d.ts.map +1 -0
  44. package/dist/src/algorithms/planarity/special-graphs.js +18 -15
  45. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -1
  46. package/dist/src/generators/basic.d.ts +2 -1
  47. package/dist/src/generators/basic.d.ts.map +1 -0
  48. package/dist/src/generators/basic.js +4 -4
  49. package/dist/src/generators/basic.js.map +1 -1
  50. package/dist/src/generators/bipartite.d.ts +2 -1
  51. package/dist/src/generators/bipartite.d.ts.map +1 -0
  52. package/dist/src/generators/bipartite.js +2 -2
  53. package/dist/src/generators/bipartite.js.map +1 -1
  54. package/dist/src/generators/grid.d.ts +2 -1
  55. package/dist/src/generators/grid.d.ts.map +1 -0
  56. package/dist/src/generators/grid.js +1 -1
  57. package/dist/src/generators/grid.js.map +1 -1
  58. package/dist/src/generators/index.d.ts +6 -5
  59. package/dist/src/generators/index.d.ts.map +1 -0
  60. package/dist/src/generators/index.js +5 -5
  61. package/dist/src/generators/index.js.map +1 -1
  62. package/dist/src/generators/random.d.ts +2 -1
  63. package/dist/src/generators/random.d.ts.map +1 -0
  64. package/dist/src/generators/random.js +2 -2
  65. package/dist/src/generators/random.js.map +1 -1
  66. package/dist/src/generators/scale-free.d.ts +2 -1
  67. package/dist/src/generators/scale-free.d.ts.map +1 -0
  68. package/dist/src/generators/scale-free.js +4 -4
  69. package/dist/src/generators/scale-free.js.map +1 -1
  70. package/dist/src/index.d.ts +5 -4
  71. package/dist/src/index.d.ts.map +1 -0
  72. package/dist/src/index.js +4 -4
  73. package/dist/src/layouts/basic/index.d.ts +2 -1
  74. package/dist/src/layouts/basic/index.d.ts.map +1 -0
  75. package/dist/src/layouts/basic/index.js +1 -1
  76. package/dist/src/layouts/basic/random.d.ts +2 -2
  77. package/dist/src/layouts/basic/random.d.ts.map +1 -0
  78. package/dist/src/layouts/basic/random.js +4 -5
  79. package/dist/src/layouts/basic/random.js.map +1 -1
  80. package/dist/src/layouts/force-directed/arf.d.ts +2 -2
  81. package/dist/src/layouts/force-directed/arf.d.ts.map +1 -0
  82. package/dist/src/layouts/force-directed/arf.js +13 -8
  83. package/dist/src/layouts/force-directed/arf.js.map +1 -1
  84. package/dist/src/layouts/force-directed/forceatlas2.d.ts +4 -4
  85. package/dist/src/layouts/force-directed/forceatlas2.d.ts.map +1 -0
  86. package/dist/src/layouts/force-directed/forceatlas2.js +83 -51
  87. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -1
  88. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +12 -12
  89. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts.map +1 -0
  90. package/dist/src/layouts/force-directed/fruchterman-reingold.js +18 -18
  91. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -1
  92. package/dist/src/layouts/force-directed/index.d.ts +6 -5
  93. package/dist/src/layouts/force-directed/index.d.ts.map +1 -0
  94. package/dist/src/layouts/force-directed/index.js +5 -5
  95. package/dist/src/layouts/force-directed/index.js.map +1 -1
  96. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +3 -3
  97. package/dist/src/layouts/force-directed/kamada-kawai.d.ts.map +1 -0
  98. package/dist/src/layouts/force-directed/kamada-kawai.js +13 -11
  99. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -1
  100. package/dist/src/layouts/force-directed/spring.d.ts +12 -12
  101. package/dist/src/layouts/force-directed/spring.d.ts.map +1 -0
  102. package/dist/src/layouts/force-directed/spring.js +11 -12
  103. package/dist/src/layouts/force-directed/spring.js.map +1 -1
  104. package/dist/src/layouts/geometric/circular.d.ts +2 -2
  105. package/dist/src/layouts/geometric/circular.d.ts.map +1 -0
  106. package/dist/src/layouts/geometric/circular.js +11 -10
  107. package/dist/src/layouts/geometric/circular.js.map +1 -1
  108. package/dist/src/layouts/geometric/index.d.ts +4 -3
  109. package/dist/src/layouts/geometric/index.d.ts.map +1 -0
  110. package/dist/src/layouts/geometric/index.js +3 -3
  111. package/dist/src/layouts/geometric/shell.d.ts +2 -2
  112. package/dist/src/layouts/geometric/shell.d.ts.map +1 -0
  113. package/dist/src/layouts/geometric/shell.js +6 -6
  114. package/dist/src/layouts/geometric/shell.js.map +1 -1
  115. package/dist/src/layouts/geometric/spiral.d.ts +2 -2
  116. package/dist/src/layouts/geometric/spiral.d.ts.map +1 -0
  117. package/dist/src/layouts/geometric/spiral.js +6 -10
  118. package/dist/src/layouts/geometric/spiral.js.map +1 -1
  119. package/dist/src/layouts/hierarchical/bfs.d.ts +3 -3
  120. package/dist/src/layouts/hierarchical/bfs.d.ts.map +1 -0
  121. package/dist/src/layouts/hierarchical/bfs.js +6 -7
  122. package/dist/src/layouts/hierarchical/bfs.js.map +1 -1
  123. package/dist/src/layouts/hierarchical/bipartite.d.ts +3 -3
  124. package/dist/src/layouts/hierarchical/bipartite.d.ts.map +1 -0
  125. package/dist/src/layouts/hierarchical/bipartite.js +9 -10
  126. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -1
  127. package/dist/src/layouts/hierarchical/index.d.ts +4 -3
  128. package/dist/src/layouts/hierarchical/index.d.ts.map +1 -0
  129. package/dist/src/layouts/hierarchical/index.js +3 -3
  130. package/dist/src/layouts/hierarchical/index.js.map +1 -1
  131. package/dist/src/layouts/hierarchical/multipartite.d.ts +3 -3
  132. package/dist/src/layouts/hierarchical/multipartite.d.ts.map +1 -0
  133. package/dist/src/layouts/hierarchical/multipartite.js +9 -10
  134. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -1
  135. package/dist/src/layouts/index.d.ts +6 -5
  136. package/dist/src/layouts/index.d.ts.map +1 -0
  137. package/dist/src/layouts/index.js +5 -5
  138. package/dist/src/layouts/index.js.map +1 -1
  139. package/dist/src/layouts/specialized/index.d.ts +3 -2
  140. package/dist/src/layouts/specialized/index.d.ts.map +1 -0
  141. package/dist/src/layouts/specialized/index.js +2 -2
  142. package/dist/src/layouts/specialized/index.js.map +1 -1
  143. package/dist/src/layouts/specialized/planar.d.ts +2 -2
  144. package/dist/src/layouts/specialized/planar.d.ts.map +1 -0
  145. package/dist/src/layouts/specialized/planar.js +6 -7
  146. package/dist/src/layouts/specialized/planar.js.map +1 -1
  147. package/dist/src/layouts/specialized/spectral.d.ts +2 -2
  148. package/dist/src/layouts/specialized/spectral.d.ts.map +1 -0
  149. package/dist/src/layouts/specialized/spectral.js +31 -21
  150. package/dist/src/layouts/specialized/spectral.js.map +1 -1
  151. package/dist/src/types/embedding.d.ts +2 -1
  152. package/dist/src/types/embedding.d.ts.map +1 -0
  153. package/dist/src/types/graph.d.ts +2 -1
  154. package/dist/src/types/graph.d.ts.map +1 -0
  155. package/dist/src/types/index.d.ts +4 -3
  156. package/dist/src/types/index.d.ts.map +1 -0
  157. package/dist/src/types/index.js +3 -3
  158. package/dist/src/types/index.js.map +1 -1
  159. package/dist/src/types/layout.d.ts +2 -1
  160. package/dist/src/types/layout.d.ts.map +1 -0
  161. package/dist/src/utils/graph.d.ts +2 -5
  162. package/dist/src/utils/graph.d.ts.map +1 -0
  163. package/dist/src/utils/graph.js +4 -6
  164. package/dist/src/utils/graph.js.map +1 -1
  165. package/dist/src/utils/index.d.ts +6 -5
  166. package/dist/src/utils/index.d.ts.map +1 -0
  167. package/dist/src/utils/index.js +5 -5
  168. package/dist/src/utils/index.js.map +1 -1
  169. package/dist/src/utils/numpy.d.ts +5 -4
  170. package/dist/src/utils/numpy.d.ts.map +1 -0
  171. package/dist/src/utils/numpy.js +22 -16
  172. package/dist/src/utils/numpy.js.map +1 -1
  173. package/dist/src/utils/params.d.ts +2 -2
  174. package/dist/src/utils/params.d.ts.map +1 -0
  175. package/dist/src/utils/params.js +0 -1
  176. package/dist/src/utils/params.js.map +1 -1
  177. package/dist/src/utils/random.d.ts +18 -0
  178. package/dist/src/utils/random.d.ts.map +1 -0
  179. package/dist/src/utils/random.js +18 -1
  180. package/dist/src/utils/random.js.map +1 -1
  181. package/dist/src/utils/rescale.d.ts +2 -3
  182. package/dist/src/utils/rescale.d.ts.map +1 -0
  183. package/dist/src/utils/rescale.js +12 -10
  184. package/dist/src/utils/rescale.js.map +1 -1
  185. package/dist/tsconfig.tsbuildinfo +1 -0
  186. package/package.json +35 -34
  187. package/src/algorithms/index.ts +2 -2
  188. package/src/algorithms/optimization/index.ts +4 -8
  189. package/src/algorithms/optimization/kamada-kawai-solver.ts +170 -179
  190. package/src/algorithms/optimization/lbfgs.ts +42 -49
  191. package/src/algorithms/optimization/line-search.ts +27 -28
  192. package/src/algorithms/optimization/types.ts +2 -2
  193. package/src/algorithms/planarity/check.ts +23 -23
  194. package/src/algorithms/planarity/embedding.ts +160 -171
  195. package/src/algorithms/planarity/index.ts +4 -8
  196. package/src/algorithms/planarity/lr-test.ts +46 -46
  197. package/src/algorithms/planarity/special-graphs.ts +73 -83
  198. package/src/generators/basic.ts +57 -57
  199. package/src/generators/bipartite.ts +37 -31
  200. package/src/generators/grid.ts +28 -28
  201. package/src/generators/index.ts +5 -11
  202. package/src/generators/random.ts +26 -25
  203. package/src/generators/scale-free.ts +56 -55
  204. package/src/index.ts +5 -5
  205. package/src/layouts/basic/index.ts +1 -1
  206. package/src/layouts/basic/random.ts +20 -16
  207. package/src/layouts/force-directed/arf.ts +107 -105
  208. package/src/layouts/force-directed/forceatlas2.ts +374 -352
  209. package/src/layouts/force-directed/fruchterman-reingold.ts +133 -134
  210. package/src/layouts/force-directed/index.ts +5 -5
  211. package/src/layouts/force-directed/kamada-kawai.ts +77 -80
  212. package/src/layouts/force-directed/spring.ts +24 -25
  213. package/src/layouts/geometric/circular.ts +63 -57
  214. package/src/layouts/geometric/index.ts +3 -3
  215. package/src/layouts/geometric/shell.ts +58 -53
  216. package/src/layouts/geometric/spiral.ts +60 -64
  217. package/src/layouts/hierarchical/bfs.ts +58 -59
  218. package/src/layouts/hierarchical/bipartite.ts +62 -63
  219. package/src/layouts/hierarchical/index.ts +3 -3
  220. package/src/layouts/hierarchical/multipartite.ts +62 -63
  221. package/src/layouts/index.ts +5 -5
  222. package/src/layouts/specialized/index.ts +2 -2
  223. package/src/layouts/specialized/planar.ts +41 -42
  224. package/src/layouts/specialized/spectral.ts +110 -99
  225. package/src/types/embedding.ts +5 -5
  226. package/src/types/graph.ts +4 -4
  227. package/src/types/index.ts +3 -3
  228. package/src/types/layout.ts +2 -2
  229. package/src/utils/graph.ts +31 -35
  230. package/src/utils/index.ts +5 -5
  231. package/src/utils/numpy.ts +96 -92
  232. package/src/utils/params.ts +14 -11
  233. package/src/utils/random.ts +57 -40
  234. package/src/utils/rescale.ts +169 -174
  235. package/dist/vitest.config.d.ts +0 -2
  236. package/dist/vitest.config.js +0 -30
  237. package/dist/vitest.config.js.map +0 -1
@@ -2,155 +2,146 @@
2
2
  * Kamada-Kawai layout algorithm optimization functions
3
3
  */
4
4
 
5
- import { Graph, Node, Edge } from '../../types';
6
- import { DistanceMap } from './types';
7
- import { _lbfgsDirection } from './lbfgs';
8
- import { _backtrackingLineSearch } from './line-search';
9
- import { getNodesFromGraph, getEdgesFromGraph } from '../../utils/graph';
5
+ import type { Graph } from "../../types";
6
+ import { getEdgesFromGraph,getNodesFromGraph } from "../../utils/graph";
7
+ import { _lbfgsDirection } from "./lbfgs";
8
+ import { _backtrackingLineSearch } from "./line-search";
9
+ import { DistanceMap } from "./types";
10
10
 
11
11
  /**
12
12
  * Compute all-pairs shortest path distances for the graph
13
- *
14
13
  * @param G - NetworkX graph
15
14
  * @param weight - Edge attribute for weight
16
15
  * @returns Dictionary of dictionaries of shortest path distances
17
16
  */
18
- export function _computeShortestPathDistances(
19
- G: Graph,
20
- weight: string
21
- ): DistanceMap {
22
- const distances: DistanceMap = {};
23
- const nodes = getNodesFromGraph(G);
24
- const edges = getEdgesFromGraph(G);
25
-
26
- // Initialize distances with direct edges
27
- for (const node of nodes) {
28
- distances[node] = {};
29
- distances[node][node] = 0;
30
-
31
- for (const other of nodes) {
32
- if (node !== other) {
33
- distances[node][other] = Infinity;
34
- }
35
- }
36
- }
37
-
38
- // Add direct edges
39
- for (const [source, target] of edges) {
40
- // In a real implementation, we would get the weight from the graph
41
- // For now, assume weight = 1 or use weight attribute if available
42
- let edgeWeight = 1;
43
- if (G.getEdgeData) {
44
- edgeWeight = G.getEdgeData(source, target, weight) || 1;
17
+ export function _computeShortestPathDistances(G: Graph, weight: string): DistanceMap {
18
+ const distances: DistanceMap = {};
19
+ const nodes = getNodesFromGraph(G);
20
+ const edges = getEdgesFromGraph(G);
21
+
22
+ // Initialize distances with direct edges
23
+ for (const node of nodes) {
24
+ distances[node] = {};
25
+ distances[node][node] = 0;
26
+
27
+ for (const other of nodes) {
28
+ if (node !== other) {
29
+ distances[node][other] = Infinity;
30
+ }
31
+ }
45
32
  }
46
33
 
47
- distances[source][target] = edgeWeight;
48
- distances[target][source] = edgeWeight; // Assuming undirected graph
49
- }
34
+ // Add direct edges
35
+ for (const [source, target] of edges) {
36
+ // In a real implementation, we would get the weight from the graph
37
+ // For now, assume weight = 1 or use weight attribute if available
38
+ let edgeWeight = 1;
39
+ if (G.getEdgeData) {
40
+ edgeWeight = G.getEdgeData(source, target, weight) || 1;
41
+ }
42
+
43
+ distances[source][target] = edgeWeight;
44
+ distances[target][source] = edgeWeight; // Assuming undirected graph
45
+ }
50
46
 
51
- // Floyd-Warshall algorithm for all-pairs shortest paths
52
- for (const k of nodes) {
53
- for (const i of nodes) {
54
- for (const j of nodes) {
55
- if (distances[i][k] + distances[k][j] < distances[i][j]) {
56
- distances[i][j] = distances[i][k] + distances[k][j];
47
+ // Floyd-Warshall algorithm for all-pairs shortest paths
48
+ for (const k of nodes) {
49
+ for (const i of nodes) {
50
+ for (const j of nodes) {
51
+ if (distances[i][k] + distances[k][j] < distances[i][j]) {
52
+ distances[i][j] = distances[i][k] + distances[k][j];
53
+ }
54
+ }
57
55
  }
58
- }
59
56
  }
60
- }
61
57
 
62
- return distances;
58
+ return distances;
63
59
  }
64
60
 
65
61
  /**
66
62
  * Solve the Kamada-Kawai layout optimization problem
67
- *
68
63
  * @param distMatrix - Matrix of desired distances between nodes
69
64
  * @param positions - Initial node positions
70
65
  * @param dim - Dimension of layout
71
66
  * @returns Optimized node positions
72
67
  */
73
- export function _kamadaKawaiSolve(
74
- distMatrix: number[][],
75
- positions: number[][],
76
- dim: number
77
- ): number[][] {
78
- // Implementation of L-BFGS optimization for Kamada-Kawai
79
- const nNodes = positions.length;
80
- const meanWeight = 1e-3;
81
-
82
- // Convert distances to inverse distances (with protection against division by zero)
83
- const invDistMatrix = distMatrix.map(row =>
84
- row.map(d => d === 0 ? 0 : 1 / (d + 1e-3))
85
- );
86
-
87
- // Flatten positions for optimization
88
- let posVec = positions.flat();
89
-
90
- // Optimization parameters
91
- const maxIter = 500;
92
- const gtol = 1e-5;
93
- const m = 10; // L-BFGS memory size
94
-
95
- // Implement a simplified L-BFGS-B algorithm
96
- let alpha = 1.0;
97
- const oldValues: number[][] = [];
98
- const oldGrads: number[][] = [];
99
-
100
- for (let iter = 0; iter < maxIter; iter++) {
101
- // Calculate cost and gradient
102
- const [cost, grad] = _kamadaKawaiCostfn(posVec, invDistMatrix, meanWeight, dim);
103
-
104
- // Compute search direction using L-BFGS approximation
105
- const direction = _lbfgsDirection(grad, oldValues, oldGrads, m);
106
-
107
- // Simple line search for step size
108
- alpha = _backtrackingLineSearch(
109
- posVec, direction, cost, grad,
110
- (x: number[]) => _kamadaKawaiCostfn(x, invDistMatrix, meanWeight, dim)[0],
111
- alpha
112
- );
113
-
114
- // Save current position and gradient for next iteration
115
- const oldPos = [...posVec];
116
-
117
- // Update position
118
- for (let i = 0; i < posVec.length; i++) {
119
- posVec[i] += alpha * direction[i];
120
- }
68
+ export function _kamadaKawaiSolve(distMatrix: number[][], positions: number[][], dim: number): number[][] {
69
+ // Implementation of L-BFGS optimization for Kamada-Kawai
70
+ const nNodes = positions.length;
71
+ const meanWeight = 1e-3;
72
+
73
+ // Convert distances to inverse distances (with protection against division by zero)
74
+ const invDistMatrix = distMatrix.map((row) => row.map((d) => (d === 0 ? 0 : 1 / (d + 1e-3))));
75
+
76
+ // Flatten positions for optimization
77
+ const posVec = positions.flat();
78
+
79
+ // Optimization parameters
80
+ const maxIter = 500;
81
+ const gtol = 1e-5;
82
+ const m = 10; // L-BFGS memory size
83
+
84
+ // Implement a simplified L-BFGS-B algorithm
85
+ let alpha = 1.0;
86
+ const oldValues: number[][] = [];
87
+ const oldGrads: number[][] = [];
88
+
89
+ for (let iter = 0; iter < maxIter; iter++) {
90
+ // Calculate cost and gradient
91
+ const [cost, grad] = _kamadaKawaiCostfn(posVec, invDistMatrix, meanWeight, dim);
92
+
93
+ // Compute search direction using L-BFGS approximation
94
+ const direction = _lbfgsDirection(grad, oldValues, oldGrads, m);
95
+
96
+ // Simple line search for step size
97
+ alpha = _backtrackingLineSearch(
98
+ posVec,
99
+ direction,
100
+ cost,
101
+ grad,
102
+ (x: number[]) => _kamadaKawaiCostfn(x, invDistMatrix, meanWeight, dim)[0],
103
+ alpha,
104
+ );
105
+
106
+ // Save current position and gradient for next iteration
107
+ const oldPos = [...posVec];
108
+
109
+ // Update position
110
+ for (let i = 0; i < posVec.length; i++) {
111
+ posVec[i] += alpha * direction[i];
112
+ }
121
113
 
122
- // Calculate new gradient
123
- const [, newGrad] = _kamadaKawaiCostfn(posVec, invDistMatrix, meanWeight, dim);
114
+ // Calculate new gradient
115
+ const [, newGrad] = _kamadaKawaiCostfn(posVec, invDistMatrix, meanWeight, dim);
124
116
 
125
- // Update L-BFGS memory
126
- oldValues.push(posVec.map((val, i) => val - oldPos[i]));
127
- oldGrads.push(newGrad.map((val, i) => val - grad[i]));
117
+ // Update L-BFGS memory
118
+ oldValues.push(posVec.map((val, i) => val - oldPos[i]));
119
+ oldGrads.push(newGrad.map((val, i) => val - grad[i]));
128
120
 
129
- // Keep only m most recent updates
130
- if (oldValues.length > m) {
131
- oldValues.shift();
132
- oldGrads.shift();
133
- }
121
+ // Keep only m most recent updates
122
+ if (oldValues.length > m) {
123
+ oldValues.shift();
124
+ oldGrads.shift();
125
+ }
134
126
 
135
- // Check convergence
136
- const gradNorm = Math.sqrt(newGrad.reduce((sum, g) => sum + g * g, 0));
137
- if (gradNorm < gtol) {
138
- break;
127
+ // Check convergence
128
+ const gradNorm = Math.sqrt(newGrad.reduce((sum, g) => sum + g * g, 0));
129
+ if (gradNorm < gtol) {
130
+ break;
131
+ }
139
132
  }
140
- }
141
133
 
142
- // Reshape result back into positions array
143
- const result: number[][] = [];
144
- for (let i = 0; i < nNodes; i++) {
145
- result.push(posVec.slice(i * dim, (i + 1) * dim));
146
- }
134
+ // Reshape result back into positions array
135
+ const result: number[][] = [];
136
+ for (let i = 0; i < nNodes; i++) {
137
+ result.push(posVec.slice(i * dim, (i + 1) * dim));
138
+ }
147
139
 
148
- return result;
140
+ return result;
149
141
  }
150
142
 
151
143
  /**
152
144
  * Cost function and gradient for Kamada-Kawai layout algorithm
153
- *
154
145
  * @param posVec - Flattened position array
155
146
  * @param invDist - Inverse distance matrix
156
147
  * @param meanWeight - Weight for centering positions
@@ -158,74 +149,74 @@ export function _kamadaKawaiSolve(
158
149
  * @returns Array with [cost, gradient]
159
150
  */
160
151
  export function _kamadaKawaiCostfn(
161
- posVec: number[],
162
- invDist: number[][],
163
- meanWeight: number,
164
- dim: number
152
+ posVec: number[],
153
+ invDist: number[][],
154
+ meanWeight: number,
155
+ dim: number,
165
156
  ): [number, number[]] {
166
- const nNodes = invDist.length;
167
- const positions: number[][] = [];
168
-
169
- // Reshape flat vector into positions array
170
- for (let i = 0; i < nNodes; i++) {
171
- positions.push(posVec.slice(i * dim, (i + 1) * dim));
172
- }
173
-
174
- // Calculate cost
175
- let cost = 0;
176
-
177
- // Add mean position penalty term
178
- const sumPos = Array(dim).fill(0);
179
- for (let i = 0; i < nNodes; i++) {
180
- for (let d = 0; d < dim; d++) {
181
- sumPos[d] += positions[i][d];
157
+ const nNodes = invDist.length;
158
+ const positions: number[][] = [];
159
+
160
+ // Reshape flat vector into positions array
161
+ for (let i = 0; i < nNodes; i++) {
162
+ positions.push(posVec.slice(i * dim, (i + 1) * dim));
163
+ }
164
+
165
+ // Calculate cost
166
+ let cost = 0;
167
+
168
+ // Add mean position penalty term
169
+ const sumPos: number[] = Array(dim).fill(0);
170
+ for (let i = 0; i < nNodes; i++) {
171
+ for (let d = 0; d < dim; d++) {
172
+ sumPos[d] += positions[i][d];
173
+ }
182
174
  }
183
- }
184
- cost += 0.5 * meanWeight * sumPos.reduce((sum, val) => sum + val * val, 0);
185
-
186
- // Add distance penalty terms
187
- for (let i = 0; i < nNodes; i++) {
188
- for (let j = i + 1; j < nNodes; j++) {
189
- // Calculate actual distance
190
- const diff = positions[i].map((val, d) => val - positions[j][d]);
191
- const distance = Math.sqrt(diff.reduce((sum, d) => sum + d * d, 0));
192
-
193
- // Add penalty for difference between actual and ideal distance
194
- const idealInvDist = invDist[i][j];
195
- const offset = distance * idealInvDist - 1.0;
196
- cost += 0.5 * offset * offset;
175
+ cost += 0.5 * meanWeight * sumPos.reduce((sum: number, val: number) => sum + val * val, 0);
176
+
177
+ // Add distance penalty terms
178
+ for (let i = 0; i < nNodes; i++) {
179
+ for (let j = i + 1; j < nNodes; j++) {
180
+ // Calculate actual distance
181
+ const diff = positions[i].map((val, d) => val - positions[j][d]);
182
+ const distance = Math.sqrt(diff.reduce((sum, d) => sum + d * d, 0));
183
+
184
+ // Add penalty for difference between actual and ideal distance
185
+ const idealInvDist = invDist[i][j];
186
+ const offset = distance * idealInvDist - 1.0;
187
+ cost += 0.5 * offset * offset;
188
+ }
197
189
  }
198
- }
199
190
 
200
- // Calculate gradient
201
- const grad = new Array(posVec.length).fill(0);
191
+ // Calculate gradient
192
+ const grad = new Array(posVec.length).fill(0);
202
193
 
203
- // Add gradient of mean position penalty
204
- for (let i = 0; i < nNodes; i++) {
205
- for (let d = 0; d < dim; d++) {
206
- grad[i * dim + d] += meanWeight * sumPos[d];
194
+ // Add gradient of mean position penalty
195
+ for (let i = 0; i < nNodes; i++) {
196
+ for (let d = 0; d < dim; d++) {
197
+ (grad[i * dim + d] as number) += meanWeight * sumPos[d];
198
+ }
207
199
  }
208
- }
209
-
210
- // Add gradient of distance penalties
211
- for (let i = 0; i < nNodes; i++) {
212
- for (let j = i + 1; j < nNodes; j++) {
213
- // Calculate actual distance and direction
214
- const diff = positions[i].map((val, d) => val - positions[j][d]);
215
- const distance = Math.sqrt(diff.reduce((sum, d) => sum + d * d, 0)) || 1e-10;
216
- const direction = diff.map(d => d / distance);
217
-
218
- // Calculate contribution to gradient
219
- const idealInvDist = invDist[i][j];
220
- const offset = distance * idealInvDist - 1.0;
221
-
222
- for (let d = 0; d < dim; d++) {
223
- const force = idealInvDist * offset * direction[d];
224
- grad[i * dim + d] += force;
225
- grad[j * dim + d] -= force;
226
- }
200
+
201
+ // Add gradient of distance penalties
202
+ for (let i = 0; i < nNodes; i++) {
203
+ for (let j = i + 1; j < nNodes; j++) {
204
+ // Calculate actual distance and direction
205
+ const diff = positions[i].map((val, d) => val - positions[j][d]);
206
+ const distance = Math.sqrt(diff.reduce((sum, d) => sum + d * d, 0)) || 1e-10;
207
+ const direction = diff.map((d) => d / distance);
208
+
209
+ // Calculate contribution to gradient
210
+ const idealInvDist = invDist[i][j];
211
+ const offset = distance * idealInvDist - 1.0;
212
+
213
+ for (let d = 0; d < dim; d++) {
214
+ const force = idealInvDist * offset * direction[d];
215
+ (grad[i * dim + d] as number) += force;
216
+ (grad[j * dim + d] as number) -= force;
217
+ }
218
+ }
227
219
  }
228
- }
229
220
 
230
- return [cost, grad];
231
- }
221
+ return [cost, grad];
222
+ }
@@ -4,65 +4,58 @@
4
4
 
5
5
  /**
6
6
  * Compute the search direction using L-BFGS approximation
7
- *
8
7
  * @param grad - Current gradient
9
8
  * @param sList - List of position differences (s_k)
10
9
  * @param yList - List of gradient differences (y_k)
11
- * @param m - Memory size
10
+ * @param _m - Memory size (unused, kept for API compatibility)
12
11
  * @returns Direction vector
13
12
  */
14
- export function _lbfgsDirection(
15
- grad: number[],
16
- sList: number[][],
17
- yList: number[][],
18
- m: number
19
- ): number[] {
20
- if (sList.length === 0) {
21
- // First iteration - use negative gradient
22
- return grad.map(g => -g);
23
- }
24
-
25
- const q = grad.slice();
26
- const alpha = Array(sList.length).fill(0);
27
- const rho: number[] = [];
13
+ export function _lbfgsDirection(grad: number[], sList: number[][], yList: number[][], _m: number): number[] {
14
+ if (sList.length === 0) {
15
+ // First iteration - use negative gradient
16
+ return grad.map((g) => -g);
17
+ }
28
18
 
29
- // Compute rho values
30
- for (let i = 0; i < sList.length; i++) {
31
- const s = sList[i];
32
- const y = yList[i];
33
- rho.push(1 / y.reduce((sum, val, j) => sum + val * s[j], 0));
34
- }
19
+ const q = grad.slice();
20
+ const alpha = Array(sList.length).fill(0);
21
+ const rho: number[] = [];
35
22
 
36
- // Forward pass
37
- for (let i = sList.length - 1; i >= 0; i--) {
38
- const s = sList[i];
39
- alpha[i] = rho[i] * s.reduce((sum, val, j) => sum + val * q[j], 0);
40
- for (let j = 0; j < q.length; j++) {
41
- q[j] -= alpha[i] * yList[i][j];
23
+ // Compute rho values
24
+ for (let i = 0; i < sList.length; i++) {
25
+ const s = sList[i];
26
+ const y = yList[i];
27
+ rho.push(1 / y.reduce((sum, val, j) => sum + val * s[j], 0));
42
28
  }
43
- }
44
29
 
45
- // Scale initial Hessian approximation
46
- let gamma = 1;
47
- if (sList.length > 0 && yList.length > 0) {
48
- const y = yList[yList.length - 1];
49
- const s = sList[sList.length - 1];
50
- gamma = s.reduce((sum, val, i) => sum + val * y[i], 0) /
51
- y.reduce((sum, val) => sum + val * val, 0);
52
- }
30
+ // Forward pass
31
+ for (let i = sList.length - 1; i >= 0; i--) {
32
+ const s = sList[i];
33
+ alpha[i] = rho[i] * s.reduce((sum, val, j) => sum + val * q[j], 0);
34
+ for (let j = 0; j < q.length; j++) {
35
+ q[j] -= alpha[i] * yList[i][j];
36
+ }
37
+ }
53
38
 
54
- // Initialize direction with scaled negative gradient
55
- const direction = q.map(val => -gamma * val);
39
+ // Scale initial Hessian approximation
40
+ let gamma = 1;
41
+ if (sList.length > 0 && yList.length > 0) {
42
+ const y = yList[yList.length - 1];
43
+ const s = sList[sList.length - 1];
44
+ gamma = s.reduce((sum, val, i) => sum + val * y[i], 0) / y.reduce((sum, val) => sum + val * val, 0);
45
+ }
56
46
 
57
- // Backward pass
58
- for (let i = 0; i < sList.length; i++) {
59
- const s = sList[i];
60
- const y = yList[i];
61
- const beta = rho[i] * y.reduce((sum, val, j) => sum + val * direction[j], 0);
62
- for (let j = 0; j < direction.length; j++) {
63
- direction[j] += s[j] * (alpha[i] - beta);
47
+ // Initialize direction with scaled negative gradient
48
+ const direction = q.map((val) => -gamma * val);
49
+
50
+ // Backward pass
51
+ for (let i = 0; i < sList.length; i++) {
52
+ const s = sList[i];
53
+ const y = yList[i];
54
+ const beta = rho[i] * y.reduce((sum, val, j) => sum + val * direction[j], 0);
55
+ for (let j = 0; j < direction.length; j++) {
56
+ direction[j] += s[j] * (alpha[i] - beta);
57
+ }
64
58
  }
65
- }
66
59
 
67
- return direction;
68
- }
60
+ return direction;
61
+ }
@@ -4,7 +4,6 @@
4
4
 
5
5
  /**
6
6
  * Backtracking line search to find step size
7
- *
8
7
  * @param x - Current position
9
8
  * @param direction - Search direction
10
9
  * @param f - Function value at current position
@@ -14,37 +13,37 @@
14
13
  * @returns Optimal step size
15
14
  */
16
15
  export function _backtrackingLineSearch(
17
- x: number[],
18
- direction: number[],
19
- f: number,
20
- grad: number[],
21
- func: (x: number[]) => number,
22
- alpha0: number
16
+ x: number[],
17
+ direction: number[],
18
+ f: number,
19
+ grad: number[],
20
+ func: (x: number[]) => number,
21
+ alpha0: number,
23
22
  ): number {
24
- const c1 = 1e-4;
25
- const c2 = 0.9;
26
- const initialSlope = grad.reduce((sum, g, i) => sum + g * direction[i], 0);
23
+ const c1 = 1e-4;
24
+ const c2 = 0.9;
25
+ const initialSlope = grad.reduce((sum, g, i) => sum + g * direction[i], 0);
27
26
 
28
- if (initialSlope >= 0) {
29
- return 1e-8; // Direction is not a descent direction
30
- }
27
+ if (initialSlope >= 0) {
28
+ return 1e-8; // Direction is not a descent direction
29
+ }
31
30
 
32
- let alpha = alpha0;
33
- const maxIter = 20;
31
+ let alpha = alpha0;
32
+ const maxIter = 20;
34
33
 
35
- for (let i = 0; i < maxIter; i++) {
36
- // Try step
37
- const newX = x.map((val, i) => val + alpha * direction[i]);
38
- const newF = func(newX);
34
+ for (let i = 0; i < maxIter; i++) {
35
+ // Try step
36
+ const newX = x.map((val, i) => val + alpha * direction[i]);
37
+ const newF = func(newX);
39
38
 
40
- // Check sufficient decrease condition (Armijo condition)
41
- if (newF <= f + c1 * alpha * initialSlope) {
42
- return alpha;
43
- }
39
+ // Check sufficient decrease condition (Armijo condition)
40
+ if (newF <= f + c1 * alpha * initialSlope) {
41
+ return alpha;
42
+ }
44
43
 
45
- // Reduce step size
46
- alpha *= c2;
47
- }
44
+ // Reduce step size
45
+ alpha *= c2;
46
+ }
48
47
 
49
- return alpha; // Return last alpha even if not optimal
50
- }
48
+ return alpha; // Return last alpha even if not optimal
49
+ }
@@ -2,7 +2,7 @@
2
2
  * Type definitions for optimization algorithms
3
3
  */
4
4
 
5
- import { Node } from '../../types';
5
+ import { Node } from "../../types";
6
6
 
7
7
  // Type definitions for distance structure used in Kamada-Kawai
8
- export type DistanceMap = Record<Node, Record<Node, number>>;
8
+ export type DistanceMap = Record<Node, Record<Node, number>>;