@graphty/layout 1.2.2 → 1.2.4

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 (200) hide show
  1. package/README.md +317 -237
  2. package/dist/src/algorithms/index.d.ts +5 -0
  3. package/dist/src/algorithms/index.js +6 -0
  4. package/dist/src/algorithms/index.js.map +1 -0
  5. package/dist/src/algorithms/optimization/index.d.ts +7 -0
  6. package/dist/src/algorithms/optimization/index.js +7 -0
  7. package/dist/src/algorithms/optimization/index.js.map +1 -0
  8. package/dist/src/algorithms/optimization/kamada-kawai-solver.d.ts +32 -0
  9. package/dist/src/algorithms/optimization/kamada-kawai-solver.js +176 -0
  10. package/dist/src/algorithms/optimization/kamada-kawai-solver.js.map +1 -0
  11. package/dist/src/algorithms/optimization/lbfgs.d.ts +13 -0
  12. package/dist/src/algorithms/optimization/lbfgs.js +56 -0
  13. package/dist/src/algorithms/optimization/lbfgs.js.map +1 -0
  14. package/dist/src/algorithms/optimization/line-search.d.ts +15 -0
  15. package/dist/src/algorithms/optimization/line-search.js +37 -0
  16. package/dist/src/algorithms/optimization/line-search.js.map +1 -0
  17. package/dist/src/algorithms/optimization/types.d.ts +5 -0
  18. package/dist/src/algorithms/optimization/types.js +5 -0
  19. package/dist/src/algorithms/optimization/types.js.map +1 -0
  20. package/dist/src/algorithms/planarity/check.d.ts +17 -0
  21. package/dist/src/algorithms/planarity/check.js +30 -0
  22. package/dist/src/algorithms/planarity/check.js.map +1 -0
  23. package/dist/src/algorithms/planarity/embedding.d.ts +29 -0
  24. package/dist/src/algorithms/planarity/embedding.js +179 -0
  25. package/dist/src/algorithms/planarity/embedding.js.map +1 -0
  26. package/dist/src/algorithms/planarity/index.d.ts +7 -0
  27. package/dist/src/algorithms/planarity/index.js +8 -0
  28. package/dist/src/algorithms/planarity/index.js.map +1 -0
  29. package/dist/src/algorithms/planarity/lr-test.d.ts +15 -0
  30. package/dist/src/algorithms/planarity/lr-test.js +54 -0
  31. package/dist/src/algorithms/planarity/lr-test.js.map +1 -0
  32. package/dist/src/algorithms/planarity/special-graphs.d.ts +28 -0
  33. package/dist/src/algorithms/planarity/special-graphs.js +108 -0
  34. package/dist/src/algorithms/planarity/special-graphs.js.map +1 -0
  35. package/dist/src/generators/basic.d.ts +28 -0
  36. package/dist/src/generators/basic.js +79 -0
  37. package/dist/src/generators/basic.js.map +1 -0
  38. package/dist/src/generators/bipartite.d.ts +16 -0
  39. package/dist/src/generators/bipartite.js +40 -0
  40. package/dist/src/generators/bipartite.js.map +1 -0
  41. package/dist/src/generators/grid.d.ts +11 -0
  42. package/dist/src/generators/grid.js +37 -0
  43. package/dist/src/generators/grid.js.map +1 -0
  44. package/dist/src/generators/index.d.ts +8 -0
  45. package/dist/src/generators/index.js +9 -0
  46. package/dist/src/generators/index.js.map +1 -0
  47. package/dist/src/generators/random.d.ts +12 -0
  48. package/dist/src/generators/random.js +34 -0
  49. package/dist/src/generators/random.js.map +1 -0
  50. package/dist/src/generators/scale-free.d.ts +12 -0
  51. package/dist/src/generators/scale-free.js +62 -0
  52. package/dist/src/generators/scale-free.js.map +1 -0
  53. package/dist/src/index.d.ts +10 -0
  54. package/dist/src/index.js +15 -0
  55. package/dist/src/index.js.map +1 -0
  56. package/dist/src/layouts/basic/index.d.ts +4 -0
  57. package/dist/src/layouts/basic/index.js +5 -0
  58. package/dist/src/layouts/basic/index.js.map +1 -0
  59. package/dist/src/layouts/basic/random.d.ts +14 -0
  60. package/dist/src/layouts/basic/random.js +27 -0
  61. package/dist/src/layouts/basic/random.js.map +1 -0
  62. package/dist/src/layouts/force-directed/arf.d.ts +13 -0
  63. package/dist/src/layouts/force-directed/arf.js +103 -0
  64. package/dist/src/layouts/force-directed/arf.js.map +1 -0
  65. package/dist/src/layouts/force-directed/forceatlas2.d.ts +22 -0
  66. package/dist/src/layouts/force-directed/forceatlas2.js +322 -0
  67. package/dist/src/layouts/force-directed/forceatlas2.js.map +1 -0
  68. package/dist/src/layouts/force-directed/fruchterman-reingold.d.ts +19 -0
  69. package/dist/src/layouts/force-directed/fruchterman-reingold.js +131 -0
  70. package/dist/src/layouts/force-directed/fruchterman-reingold.js.map +1 -0
  71. package/dist/src/layouts/force-directed/index.d.ts +8 -0
  72. package/dist/src/layouts/force-directed/index.js +9 -0
  73. package/dist/src/layouts/force-directed/index.js.map +1 -0
  74. package/dist/src/layouts/force-directed/kamada-kawai.d.ts +15 -0
  75. package/dist/src/layouts/force-directed/kamada-kawai.js +87 -0
  76. package/dist/src/layouts/force-directed/kamada-kawai.js.map +1 -0
  77. package/dist/src/layouts/force-directed/spring.d.ts +19 -0
  78. package/dist/src/layouts/force-directed/spring.js +23 -0
  79. package/dist/src/layouts/force-directed/spring.js.map +1 -0
  80. package/dist/src/layouts/geometric/circular.d.ts +14 -0
  81. package/dist/src/layouts/geometric/circular.js +67 -0
  82. package/dist/src/layouts/geometric/circular.js.map +1 -0
  83. package/dist/src/layouts/geometric/index.d.ts +6 -0
  84. package/dist/src/layouts/geometric/index.js +7 -0
  85. package/dist/src/layouts/geometric/index.js.map +1 -0
  86. package/dist/src/layouts/geometric/shell.d.ts +15 -0
  87. package/dist/src/layouts/geometric/shell.js +67 -0
  88. package/dist/src/layouts/geometric/shell.js.map +1 -0
  89. package/dist/src/layouts/geometric/spiral.d.ts +16 -0
  90. package/dist/src/layouts/geometric/spiral.js +73 -0
  91. package/dist/src/layouts/geometric/spiral.js.map +1 -0
  92. package/dist/src/layouts/hierarchical/bfs.d.ts +12 -0
  93. package/dist/src/layouts/hierarchical/bfs.js +62 -0
  94. package/dist/src/layouts/hierarchical/bfs.js.map +1 -0
  95. package/dist/src/layouts/hierarchical/bipartite.d.ts +16 -0
  96. package/dist/src/layouts/hierarchical/bipartite.js +72 -0
  97. package/dist/src/layouts/hierarchical/bipartite.js.map +1 -0
  98. package/dist/src/layouts/hierarchical/index.d.ts +6 -0
  99. package/dist/src/layouts/hierarchical/index.js +7 -0
  100. package/dist/src/layouts/hierarchical/index.js.map +1 -0
  101. package/dist/src/layouts/hierarchical/multipartite.d.ts +12 -0
  102. package/dist/src/layouts/hierarchical/multipartite.js +73 -0
  103. package/dist/src/layouts/hierarchical/multipartite.js.map +1 -0
  104. package/dist/src/layouts/index.d.ts +8 -0
  105. package/dist/src/layouts/index.js +9 -0
  106. package/dist/src/layouts/index.js.map +1 -0
  107. package/dist/src/layouts/specialized/index.d.ts +5 -0
  108. package/dist/src/layouts/specialized/index.js +6 -0
  109. package/dist/src/layouts/specialized/index.js.map +1 -0
  110. package/dist/src/layouts/specialized/planar.d.ts +14 -0
  111. package/dist/src/layouts/specialized/planar.js +47 -0
  112. package/dist/src/layouts/specialized/planar.js.map +1 -0
  113. package/dist/src/layouts/specialized/spectral.d.ts +14 -0
  114. package/dist/src/layouts/specialized/spectral.js +106 -0
  115. package/dist/src/layouts/specialized/spectral.js.map +1 -0
  116. package/dist/src/types/embedding.d.ts +9 -0
  117. package/dist/src/types/embedding.js +5 -0
  118. package/dist/src/types/embedding.js.map +1 -0
  119. package/dist/src/types/graph.d.ts +10 -0
  120. package/dist/src/types/graph.js +5 -0
  121. package/dist/src/types/graph.js.map +1 -0
  122. package/dist/src/types/index.d.ts +6 -0
  123. package/dist/src/types/index.js +7 -0
  124. package/dist/src/types/index.js.map +1 -0
  125. package/dist/src/types/layout.d.ts +6 -0
  126. package/dist/src/types/layout.js +5 -0
  127. package/dist/src/types/layout.js.map +1 -0
  128. package/dist/src/utils/graph.d.ts +34 -0
  129. package/dist/src/utils/graph.js +69 -0
  130. package/dist/src/utils/graph.js.map +1 -0
  131. package/dist/src/utils/index.d.ts +8 -0
  132. package/dist/src/utils/index.js +9 -0
  133. package/dist/src/utils/index.js.map +1 -0
  134. package/dist/src/utils/numpy.d.ts +17 -0
  135. package/dist/src/utils/numpy.js +101 -0
  136. package/dist/src/utils/numpy.js.map +1 -0
  137. package/dist/src/utils/params.d.ts +17 -0
  138. package/dist/src/utils/params.js +22 -0
  139. package/dist/src/utils/params.js.map +1 -0
  140. package/dist/src/utils/random.d.ts +14 -0
  141. package/dist/src/utils/random.js +42 -0
  142. package/dist/src/utils/random.js.map +1 -0
  143. package/dist/src/utils/rescale.d.ts +21 -0
  144. package/dist/src/utils/rescale.js +119 -0
  145. package/dist/src/utils/rescale.js.map +1 -0
  146. package/dist/vitest.config.js +1 -1
  147. package/dist/vitest.config.js.map +1 -1
  148. package/package.json +15 -1
  149. package/.env.example +0 -11
  150. package/.github/workflows/ci.yml +0 -101
  151. package/.releaserc.json +0 -22
  152. package/CHANGELOG.md +0 -45
  153. package/CLAUDE.md +0 -104
  154. package/CONTRIBUTING.md +0 -1
  155. package/DEPLOYMENT.md +0 -59
  156. package/commitlint.config.js +0 -37
  157. package/examples/3d-force-directed.html +0 -611
  158. package/examples/3d-kamada-kawai.html +0 -379
  159. package/examples/3d-layout-comparison.html +0 -448
  160. package/examples/3d-spherical-layout.html +0 -319
  161. package/examples/arf-layout.html +0 -524
  162. package/examples/bfs-layout.html +0 -487
  163. package/examples/bipartite-layout.html +0 -382
  164. package/examples/circular-layout.html +0 -292
  165. package/examples/forceatlas2-layout.html +0 -588
  166. package/examples/index.html +0 -241
  167. package/examples/kamada-kawai-layout.html +0 -425
  168. package/examples/layout-helpers.js +0 -486
  169. package/examples/multipartite-layout.html +0 -410
  170. package/examples/planar-layout.html +0 -388
  171. package/examples/random-layout.html +0 -328
  172. package/examples/shell-layout.html +0 -323
  173. package/examples/spectral-layout.html +0 -335
  174. package/examples/spiral-layout.html +0 -447
  175. package/examples/spring-layout.html +0 -313
  176. package/test/arf-layout.test.ts +0 -443
  177. package/test/bfs-layout.test.ts +0 -427
  178. package/test/bipartite-layout.test.ts +0 -344
  179. package/test/circular-layout.test.ts +0 -442
  180. package/test/forceatlas2-layout.test.ts +0 -405
  181. package/test/fruchterman-reingold-layout.test.ts +0 -477
  182. package/test/graph-generators.test.ts +0 -450
  183. package/test/kamada-kawai-layout.test.ts +0 -623
  184. package/test/multipartite-layout.test.ts +0 -404
  185. package/test/planar-layout.test.ts +0 -266
  186. package/test/random-layout.test.ts +0 -254
  187. package/test/rescale-layout.test.ts +0 -373
  188. package/test/shell-layout.test.ts +0 -347
  189. package/test/spectral-layout.test.ts +0 -378
  190. package/test/spiral-layout.test.ts +0 -338
  191. package/test/spring-layout.test.ts +0 -241
  192. package/test/test-utils.ts +0 -34
  193. package/test/utils-graph.test.ts +0 -155
  194. package/test/utils-index.test.ts +0 -77
  195. package/test/utils-numpy.test.ts +0 -272
  196. package/test/utils-params.test.ts +0 -99
  197. package/test/utils-random.test.ts +0 -229
  198. package/tsconfig.json +0 -16
  199. package/vite.config.js +0 -36
  200. package/vitest.config.ts +0 -30
@@ -1,450 +0,0 @@
1
- import { describe, it, assert } from 'vitest';
2
- import {
3
- completeGraph,
4
- cycleGraph,
5
- starGraph,
6
- wheelGraph,
7
- gridGraph,
8
- randomGraph,
9
- bipartiteGraph,
10
- scaleFreeGraph,
11
- circularLayout
12
- } from '../src';
13
-
14
- describe('Graph Generators', () => {
15
- describe('completeGraph', () => {
16
- it('should create complete graph with correct number of nodes and edges', () => {
17
- const graph = completeGraph(5);
18
-
19
- assert.equal(graph.nodes().length, 5);
20
- // Complete graph has n*(n-1)/2 edges
21
- assert.equal(graph.edges().length, 10);
22
-
23
- // Check nodes are numbered 0 to n-1
24
- assert.deepEqual(graph.nodes(), [0, 1, 2, 3, 4]);
25
- });
26
-
27
- it('should connect every pair of nodes', () => {
28
- const graph = completeGraph(4);
29
- const edges = graph.edges();
30
-
31
- // Should have edges: [0,1], [0,2], [0,3], [1,2], [1,3], [2,3]
32
- assert.equal(edges.length, 6);
33
-
34
- // Check all pairs are connected
35
- const edgeSet = new Set(edges.map(e => `${Math.min(e[0], e[1])},${Math.max(e[0], e[1])}`));
36
- assert.isTrue(edgeSet.has('0,1'));
37
- assert.isTrue(edgeSet.has('0,2'));
38
- assert.isTrue(edgeSet.has('0,3'));
39
- assert.isTrue(edgeSet.has('1,2'));
40
- assert.isTrue(edgeSet.has('1,3'));
41
- assert.isTrue(edgeSet.has('2,3'));
42
- });
43
-
44
- it('should handle edge cases', () => {
45
- // Single node
46
- const g1 = completeGraph(1);
47
- assert.equal(g1.nodes().length, 1);
48
- assert.equal(g1.edges().length, 0);
49
-
50
- // Two nodes
51
- const g2 = completeGraph(2);
52
- assert.equal(g2.nodes().length, 2);
53
- assert.equal(g2.edges().length, 1);
54
-
55
- // Large graph
56
- const g10 = completeGraph(10);
57
- assert.equal(g10.nodes().length, 10);
58
- assert.equal(g10.edges().length, 45);
59
- });
60
- });
61
-
62
- describe('cycleGraph', () => {
63
- it('should create cycle with correct number of nodes and edges', () => {
64
- const graph = cycleGraph(6);
65
-
66
- assert.equal(graph.nodes().length, 6);
67
- assert.equal(graph.edges().length, 6); // Cycle has n edges
68
-
69
- assert.deepEqual(graph.nodes(), [0, 1, 2, 3, 4, 5]);
70
- });
71
-
72
- it('should connect nodes in a cycle', () => {
73
- const graph = cycleGraph(5);
74
- const edges = graph.edges();
75
-
76
- // Check cycle connectivity
77
- const edgeSet = new Set(edges.map(e => `${Math.min(e[0], e[1])},${Math.max(e[0], e[1])}`));
78
- assert.isTrue(edgeSet.has('0,1'));
79
- assert.isTrue(edgeSet.has('1,2'));
80
- assert.isTrue(edgeSet.has('2,3'));
81
- assert.isTrue(edgeSet.has('3,4'));
82
- assert.isTrue(edgeSet.has('0,4')); // Closing the cycle
83
- });
84
-
85
- it('should handle small cycles', () => {
86
- // Triangle
87
- const g3 = cycleGraph(3);
88
- assert.equal(g3.nodes().length, 3);
89
- assert.equal(g3.edges().length, 3);
90
-
91
- // Square
92
- const g4 = cycleGraph(4);
93
- assert.equal(g4.nodes().length, 4);
94
- assert.equal(g4.edges().length, 4);
95
- });
96
- });
97
-
98
- describe('starGraph', () => {
99
- it('should create star with correct structure', () => {
100
- const graph = starGraph(5);
101
-
102
- assert.equal(graph.nodes().length, 5);
103
- assert.equal(graph.edges().length, 4); // Star has n-1 edges
104
-
105
- // Center is node 0
106
- const edges = graph.edges();
107
- edges.forEach(edge => {
108
- assert.isTrue(edge[0] === 0 || edge[1] === 0);
109
- });
110
- });
111
-
112
- it('should connect all nodes to center', () => {
113
- const graph = starGraph(6);
114
- const edges = graph.edges();
115
-
116
- // Check all leaves connected to center
117
- const connectedToCenter = new Set();
118
- edges.forEach(edge => {
119
- if (edge[0] === 0) connectedToCenter.add(edge[1]);
120
- if (edge[1] === 0) connectedToCenter.add(edge[0]);
121
- });
122
-
123
- assert.equal(connectedToCenter.size, 5);
124
- assert.deepEqual([...connectedToCenter].sort(), [1, 2, 3, 4, 5]);
125
- });
126
-
127
- it('should handle edge cases', () => {
128
- // Single node
129
- const g1 = starGraph(1);
130
- assert.equal(g1.nodes().length, 1);
131
- assert.equal(g1.edges().length, 0);
132
-
133
- // Two nodes
134
- const g2 = starGraph(2);
135
- assert.equal(g2.nodes().length, 2);
136
- assert.equal(g2.edges().length, 1);
137
- });
138
- });
139
-
140
- describe('wheelGraph', () => {
141
- it('should create wheel with correct structure', () => {
142
- const graph = wheelGraph(6);
143
-
144
- assert.equal(graph.nodes().length, 6);
145
- // Wheel has 2*(n-1) edges: n-1 spokes + n-1 rim edges
146
- assert.equal(graph.edges().length, 10);
147
- });
148
-
149
- it('should have hub connected to all rim nodes', () => {
150
- const graph = wheelGraph(5);
151
- const edges = graph.edges();
152
-
153
- // Node 0 is the hub
154
- const hubEdges = edges.filter(e => e[0] === 0 || e[1] === 0);
155
- assert.equal(hubEdges.length, 4); // Connected to all 4 rim nodes
156
-
157
- // Check rim forms a cycle
158
- const rimEdges = edges.filter(e => e[0] !== 0 && e[1] !== 0);
159
- assert.equal(rimEdges.length, 4); // 4 edges in the rim cycle
160
- });
161
-
162
- it('should handle small wheels', () => {
163
- // Smallest wheel (triangle with center)
164
- const g4 = wheelGraph(4);
165
- assert.equal(g4.nodes().length, 4);
166
- assert.equal(g4.edges().length, 6);
167
- });
168
- });
169
-
170
- describe('gridGraph', () => {
171
- it('should create grid with correct dimensions', () => {
172
- const graph = gridGraph(3, 4);
173
-
174
- assert.equal(graph.nodes().length, 12); // 3*4 nodes
175
- // Grid edges: (m-1)*n + m*(n-1)
176
- assert.equal(graph.edges().length, 17);
177
- });
178
-
179
- it('should use coordinate naming for nodes', () => {
180
- const graph = gridGraph(2, 3);
181
- const nodes = graph.nodes();
182
-
183
- assert.deepEqual(nodes.sort(), ['0,0', '0,1', '0,2', '1,0', '1,1', '1,2']);
184
- });
185
-
186
- it('should connect grid neighbors correctly', () => {
187
- const graph = gridGraph(3, 3);
188
- const edges = graph.edges();
189
-
190
- // Check some specific connections
191
- const edgeStrings = edges.map(e => `${e[0]}-${e[1]}`);
192
-
193
- // Horizontal connections
194
- assert.isTrue(edgeStrings.includes('0,0-0,1') || edgeStrings.includes('0,1-0,0'));
195
- assert.isTrue(edgeStrings.includes('0,1-0,2') || edgeStrings.includes('0,2-0,1'));
196
-
197
- // Vertical connections
198
- assert.isTrue(edgeStrings.includes('0,0-1,0') || edgeStrings.includes('1,0-0,0'));
199
- assert.isTrue(edgeStrings.includes('1,0-2,0') || edgeStrings.includes('2,0-1,0'));
200
- });
201
-
202
- it('should handle edge cases', () => {
203
- // 1x1 grid
204
- const g1 = gridGraph(1, 1);
205
- assert.equal(g1.nodes().length, 1);
206
- assert.equal(g1.edges().length, 0);
207
-
208
- // Line graphs
209
- const line = gridGraph(1, 5);
210
- assert.equal(line.nodes().length, 5);
211
- assert.equal(line.edges().length, 4);
212
-
213
- const vline = gridGraph(5, 1);
214
- assert.equal(vline.nodes().length, 5);
215
- assert.equal(vline.edges().length, 4);
216
- });
217
- });
218
-
219
- describe('randomGraph', () => {
220
- it('should create graph with specified number of nodes', () => {
221
- const graph = randomGraph(10, 0.3, 123);
222
-
223
- assert.equal(graph.nodes().length, 10);
224
- assert.deepEqual(graph.nodes(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
225
- });
226
-
227
- it('should respect edge probability', () => {
228
- // With p=0, no edges
229
- const g0 = randomGraph(10, 0, 123);
230
- assert.equal(g0.edges().length, 0);
231
-
232
- // With p=1, complete graph
233
- const g1 = randomGraph(10, 1, 123);
234
- assert.equal(g1.edges().length, 45); // 10*9/2
235
-
236
- // With p=0.5, roughly half of possible edges
237
- const g05 = randomGraph(20, 0.5, 123);
238
- const edgeCount = g05.edges().length;
239
- const maxEdges = 20 * 19 / 2;
240
- assert.isAbove(edgeCount, maxEdges * 0.3);
241
- assert.isBelow(edgeCount, maxEdges * 0.7);
242
- });
243
-
244
- it('should produce consistent results with same seed', () => {
245
- const g1 = randomGraph(15, 0.3, 42);
246
- const g2 = randomGraph(15, 0.3, 42);
247
-
248
- assert.equal(g1.edges().length, g2.edges().length);
249
-
250
- const edges1 = g1.edges().map(e => `${e[0]},${e[1]}`).sort();
251
- const edges2 = g2.edges().map(e => `${e[0]},${e[1]}`).sort();
252
- assert.deepEqual(edges1, edges2);
253
- });
254
-
255
- it('should produce different results with different seeds', () => {
256
- const g1 = randomGraph(15, 0.5, 123);
257
- const g2 = randomGraph(15, 0.5, 456);
258
-
259
- // Very unlikely to have same edge count
260
- assert.notEqual(g1.edges().length, g2.edges().length);
261
- });
262
- });
263
-
264
- describe('bipartiteGraph', () => {
265
- it('should create bipartite graph with two sets', () => {
266
- const graph = bipartiteGraph(3, 4, 0.5, 123);
267
-
268
- assert.equal(graph.nodes().length, 7); // 3 + 4
269
- assert.deepEqual(graph.setA, ['A0', 'A1', 'A2']);
270
- assert.deepEqual(graph.setB, ['B0', 'B1', 'B2', 'B3']);
271
- });
272
-
273
- it('should only connect nodes between sets', () => {
274
- const graph = bipartiteGraph(3, 3, 1.0, 123); // Complete bipartite
275
- const edges = graph.edges();
276
-
277
- assert.equal(edges.length, 9); // 3*3
278
-
279
- // Check all edges go between sets
280
- edges.forEach(edge => {
281
- const hasA = edge.some(node => (node as string).startsWith('A'));
282
- const hasB = edge.some(node => (node as string).startsWith('B'));
283
- assert.isTrue(hasA && hasB);
284
- });
285
- });
286
-
287
- it('should respect edge probability', () => {
288
- // No edges
289
- const g0 = bipartiteGraph(5, 5, 0, 123);
290
- assert.equal(g0.edges().length, 0);
291
-
292
- // All possible edges
293
- const g1 = bipartiteGraph(4, 6, 1, 123);
294
- assert.equal(g1.edges().length, 24); // 4*6
295
-
296
- // Partial edges
297
- const g05 = bipartiteGraph(10, 10, 0.5, 123);
298
- const edgeCount = g05.edges().length;
299
- assert.isAbove(edgeCount, 30); // Should have some edges
300
- assert.isBelow(edgeCount, 70); // But not all 100
301
- });
302
-
303
- it('should produce consistent results with seed', () => {
304
- const g1 = bipartiteGraph(5, 7, 0.4, 42);
305
- const g2 = bipartiteGraph(5, 7, 0.4, 42);
306
-
307
- const edges1 = g1.edges().map(e => `${e[0]}-${e[1]}`).sort();
308
- const edges2 = g2.edges().map(e => `${e[0]}-${e[1]}`).sort();
309
-
310
- assert.deepEqual(edges1, edges2);
311
- });
312
- });
313
-
314
- describe('scaleFreeGraph', () => {
315
- it('should create scale-free graph with preferential attachment', () => {
316
- const graph = scaleFreeGraph(20, 2, 123);
317
-
318
- assert.equal(graph.nodes().length, 20);
319
- // Each new node adds m edges
320
- assert.isAtLeast(graph.edges().length, 35); // Initial complete graph + added edges
321
- });
322
-
323
- it('should start with complete graph of m+1 nodes', () => {
324
- const graph = scaleFreeGraph(10, 3, 123);
325
- const edges = graph.edges();
326
-
327
- // First 4 nodes (0,1,2,3) should form complete graph
328
- const initialEdges = edges.filter(e =>
329
- e[0] <= 3 && e[1] <= 3
330
- );
331
-
332
- assert.equal(initialEdges.length, 6); // 4*3/2
333
- });
334
-
335
- it('should throw error if m >= n', () => {
336
- assert.throws(() => {
337
- scaleFreeGraph(5, 5);
338
- }, 'm must be less than n');
339
-
340
- assert.throws(() => {
341
- scaleFreeGraph(5, 6);
342
- }, 'm must be less than n');
343
- });
344
-
345
- it('should create graphs with power-law degree distribution', () => {
346
- const graph = scaleFreeGraph(100, 2, 123);
347
-
348
- // Calculate degree distribution
349
- const degrees = new Map();
350
- graph.nodes().forEach(node => {
351
- degrees.set(node, 0);
352
- });
353
-
354
- graph.edges().forEach(edge => {
355
- degrees.set(edge[0], degrees.get(edge[0]) + 1);
356
- degrees.set(edge[1], degrees.get(edge[1]) + 1);
357
- });
358
-
359
- // Should have some high-degree nodes (hubs)
360
- const degreeValues = Array.from(degrees.values());
361
- const maxDegree = Math.max(...degreeValues);
362
- const avgDegree = degreeValues.reduce((a, b) => a + b) / degreeValues.length;
363
-
364
- assert.isAbove(maxDegree, avgDegree * 2); // Should have hubs
365
- });
366
-
367
- it('should produce consistent results with seed', () => {
368
- const g1 = scaleFreeGraph(15, 2, 42);
369
- const g2 = scaleFreeGraph(15, 2, 42);
370
-
371
- assert.equal(g1.edges().length, g2.edges().length);
372
-
373
- const edges1 = g1.edges().map(e => `${Math.min(e[0], e[1])},${Math.max(e[0], e[1])}`).sort();
374
- const edges2 = g2.edges().map(e => `${Math.min(e[0], e[1])},${Math.max(e[0], e[1])}`).sort();
375
-
376
- assert.deepEqual(edges1, edges2);
377
- });
378
-
379
- it('should handle different m values', () => {
380
- const g1 = scaleFreeGraph(20, 1, 123);
381
- const g2 = scaleFreeGraph(20, 3, 123);
382
- const g3 = scaleFreeGraph(20, 5, 123);
383
-
384
- // More connections per new node means more edges
385
- assert.isBelow(g1.edges().length, g2.edges().length);
386
- assert.isBelow(g2.edges().length, g3.edges().length);
387
- });
388
- });
389
-
390
- describe('Graph generator integration', () => {
391
- it('should work with layout algorithms', () => {
392
- // Test each generator with a layout
393
- const generators = [
394
- () => completeGraph(5),
395
- () => cycleGraph(6),
396
- () => starGraph(7),
397
- () => wheelGraph(5),
398
- () => gridGraph(3, 3),
399
- () => randomGraph(10, 0.3, 123),
400
- () => bipartiteGraph(3, 4, 0.5, 123),
401
- () => scaleFreeGraph(15, 2, 123)
402
- ];
403
-
404
- generators.forEach(gen => {
405
- const graph = gen();
406
- const positions = circularLayout(graph);
407
-
408
- // Should position all nodes
409
- assert.equal(Object.keys(positions).length, graph.nodes().length);
410
- graph.nodes().forEach(node => {
411
- assert.isDefined(positions[node]);
412
- assert.equal(positions[node].length, 2);
413
- });
414
- });
415
- });
416
-
417
- it('should produce valid graph structure', () => {
418
- const generators = [
419
- () => completeGraph(5),
420
- () => cycleGraph(6),
421
- () => starGraph(7),
422
- () => wheelGraph(5),
423
- () => gridGraph(3, 3),
424
- () => randomGraph(10, 0.3, 123),
425
- () => bipartiteGraph(3, 4, 0.5, 123),
426
- () => scaleFreeGraph(15, 2, 123)
427
- ];
428
-
429
- generators.forEach(gen => {
430
- const graph = gen();
431
-
432
- // Should have nodes() and edges() methods
433
- assert.isFunction(graph.nodes);
434
- assert.isFunction(graph.edges);
435
-
436
- // Should return arrays
437
- assert.isArray(graph.nodes());
438
- assert.isArray(graph.edges());
439
-
440
- // Each edge should connect valid nodes
441
- const nodeSet = new Set(graph.nodes());
442
- graph.edges().forEach(edge => {
443
- assert.equal(edge.length, 2);
444
- assert.isTrue(nodeSet.has(edge[0]));
445
- assert.isTrue(nodeSet.has(edge[1]));
446
- });
447
- });
448
- });
449
- });
450
- });