@graphty/graph-samples 0.0.1 → 0.1.2
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.
- package/LICENSE +21 -0
- package/NOTICE +123 -0
- package/README.md +190 -1
- package/dist/chunks/build-DdCe60t3.js +47 -0
- package/dist/chunks/build-DdCe60t3.js.map +1 -0
- package/dist/chunks/meta-B6toBzaa.js +19 -0
- package/dist/chunks/meta-B6toBzaa.js.map +1 -0
- package/dist/chunks/meta-BM_YIhlA.js +28 -0
- package/dist/chunks/meta-BM_YIhlA.js.map +1 -0
- package/dist/chunks/meta-BMmtUpPx.js +30 -0
- package/dist/chunks/meta-BMmtUpPx.js.map +1 -0
- package/dist/chunks/meta-BXC7oi86.js +22 -0
- package/dist/chunks/meta-BXC7oi86.js.map +1 -0
- package/dist/chunks/meta-BaFPvhc1.js +19 -0
- package/dist/chunks/meta-BaFPvhc1.js.map +1 -0
- package/dist/chunks/meta-C58v1Oy0.js +28 -0
- package/dist/chunks/meta-C58v1Oy0.js.map +1 -0
- package/dist/chunks/meta-CQRXyexG.js +23 -0
- package/dist/chunks/meta-CQRXyexG.js.map +1 -0
- package/dist/chunks/meta-DrgM1eS0.js +23 -0
- package/dist/chunks/meta-DrgM1eS0.js.map +1 -0
- package/dist/chunks/meta-IhoaDXAV.js +19 -0
- package/dist/chunks/meta-IhoaDXAV.js.map +1 -0
- package/dist/chunks/meta-MQPbBUUB.js +29 -0
- package/dist/chunks/meta-MQPbBUUB.js.map +1 -0
- package/dist/chunks/meta-Qp4HNYAZ.js +24 -0
- package/dist/chunks/meta-Qp4HNYAZ.js.map +1 -0
- package/dist/chunks/meta-mns0uFIW.js +19 -0
- package/dist/chunks/meta-mns0uFIW.js.map +1 -0
- package/dist/datasets/celegans-neural.d.ts +1 -0
- package/dist/datasets/celegans-neural.js +7359 -0
- package/dist/datasets/celegans-neural.js.map +1 -0
- package/dist/datasets/contiguous-usa.d.ts +1 -0
- package/dist/datasets/contiguous-usa.js +501 -0
- package/dist/datasets/contiguous-usa.js.map +1 -0
- package/dist/datasets/davis-southern-women.d.ts +1 -0
- package/dist/datasets/davis-southern-women.js +235 -0
- package/dist/datasets/davis-southern-women.js.map +1 -0
- package/dist/datasets/dolphins.d.ts +1 -0
- package/dist/datasets/dolphins.js +406 -0
- package/dist/datasets/dolphins.js.map +1 -0
- package/dist/datasets/florentine-families.d.ts +1 -0
- package/dist/datasets/florentine-families.js +75 -0
- package/dist/datasets/florentine-families.js.map +1 -0
- package/dist/datasets/football.d.ts +1 -0
- package/dist/datasets/football.js +1508 -0
- package/dist/datasets/football.js.map +1 -0
- package/dist/datasets/karate.d.ts +1 -0
- package/dist/datasets/karate.js +295 -0
- package/dist/datasets/karate.js.map +1 -0
- package/dist/datasets/knuth-miles.d.ts +1 -0
- package/dist/datasets/knuth-miles.js +24933 -0
- package/dist/datasets/knuth-miles.js.map +1 -0
- package/dist/datasets/les-miserables.d.ts +1 -0
- package/dist/datasets/les-miserables.js +860 -0
- package/dist/datasets/les-miserables.js.map +1 -0
- package/dist/datasets/openflights.d.ts +1 -0
- package/dist/datasets/openflights.js +130277 -0
- package/dist/datasets/openflights.js.map +1 -0
- package/dist/datasets/political-blogs.d.ts +1 -0
- package/dist/datasets/political-blogs.js +42551 -0
- package/dist/datasets/political-blogs.js.map +1 -0
- package/dist/datasets/political-books.d.ts +1 -0
- package/dist/datasets/political-books.js +1124 -0
- package/dist/datasets/political-books.js.map +1 -0
- package/dist/generators.d.ts +1 -0
- package/dist/generators.js +4496 -0
- package/dist/generators.js.map +1 -0
- package/dist/graph-samples.d.ts +1 -0
- package/dist/graph-samples.js +142 -0
- package/dist/graph-samples.js.map +1 -0
- package/dist/src/datasets/build.d.ts +89 -0
- package/dist/src/datasets/build.d.ts.map +1 -0
- package/dist/src/datasets/build.js +53 -0
- package/dist/src/datasets/build.js.map +1 -0
- package/dist/src/datasets/catalog.d.ts +8 -0
- package/dist/src/datasets/catalog.d.ts.map +1 -0
- package/dist/src/datasets/catalog.js +34 -0
- package/dist/src/datasets/catalog.js.map +1 -0
- package/dist/src/datasets/celegans-neural/data.d.ts +3 -0
- package/dist/src/datasets/celegans-neural/data.d.ts.map +1 -0
- package/dist/src/datasets/celegans-neural/data.js +573 -0
- package/dist/src/datasets/celegans-neural/data.js.map +1 -0
- package/dist/src/datasets/celegans-neural/index.d.ts +11 -0
- package/dist/src/datasets/celegans-neural/index.d.ts.map +1 -0
- package/dist/src/datasets/celegans-neural/index.js +14 -0
- package/dist/src/datasets/celegans-neural/index.js.map +1 -0
- package/dist/src/datasets/celegans-neural/meta.d.ts +4 -0
- package/dist/src/datasets/celegans-neural/meta.d.ts.map +1 -0
- package/dist/src/datasets/celegans-neural/meta.js +21 -0
- package/dist/src/datasets/celegans-neural/meta.js.map +1 -0
- package/dist/src/datasets/contiguous-usa/data.d.ts +3 -0
- package/dist/src/datasets/contiguous-usa/data.d.ts.map +1 -0
- package/dist/src/datasets/contiguous-usa/data.js +162 -0
- package/dist/src/datasets/contiguous-usa/data.js.map +1 -0
- package/dist/src/datasets/contiguous-usa/index.d.ts +11 -0
- package/dist/src/datasets/contiguous-usa/index.d.ts.map +1 -0
- package/dist/src/datasets/contiguous-usa/index.js +14 -0
- package/dist/src/datasets/contiguous-usa/index.js.map +1 -0
- package/dist/src/datasets/contiguous-usa/meta.d.ts +4 -0
- package/dist/src/datasets/contiguous-usa/meta.d.ts.map +1 -0
- package/dist/src/datasets/contiguous-usa/meta.js +27 -0
- package/dist/src/datasets/contiguous-usa/meta.js.map +1 -0
- package/dist/src/datasets/davis-southern-women/data.d.ts +3 -0
- package/dist/src/datasets/davis-southern-women/data.d.ts.map +1 -0
- package/dist/src/datasets/davis-southern-women/data.js +57 -0
- package/dist/src/datasets/davis-southern-women/data.js.map +1 -0
- package/dist/src/datasets/davis-southern-women/index.d.ts +11 -0
- package/dist/src/datasets/davis-southern-women/index.d.ts.map +1 -0
- package/dist/src/datasets/davis-southern-women/index.js +14 -0
- package/dist/src/datasets/davis-southern-women/index.js.map +1 -0
- package/dist/src/datasets/davis-southern-women/meta.d.ts +4 -0
- package/dist/src/datasets/davis-southern-women/meta.d.ts.map +1 -0
- package/dist/src/datasets/davis-southern-women/meta.js +17 -0
- package/dist/src/datasets/davis-southern-women/meta.js.map +1 -0
- package/dist/src/datasets/dolphins/data.d.ts +3 -0
- package/dist/src/datasets/dolphins/data.d.ts.map +1 -0
- package/dist/src/datasets/dolphins/data.js +93 -0
- package/dist/src/datasets/dolphins/data.js.map +1 -0
- package/dist/src/datasets/dolphins/index.d.ts +11 -0
- package/dist/src/datasets/dolphins/index.d.ts.map +1 -0
- package/dist/src/datasets/dolphins/index.js +14 -0
- package/dist/src/datasets/dolphins/index.js.map +1 -0
- package/dist/src/datasets/dolphins/meta.d.ts +4 -0
- package/dist/src/datasets/dolphins/meta.d.ts.map +1 -0
- package/dist/src/datasets/dolphins/meta.js +17 -0
- package/dist/src/datasets/dolphins/meta.js.map +1 -0
- package/dist/src/datasets/florentine-families/data.d.ts +3 -0
- package/dist/src/datasets/florentine-families/data.d.ts.map +1 -0
- package/dist/src/datasets/florentine-families/data.js +31 -0
- package/dist/src/datasets/florentine-families/data.js.map +1 -0
- package/dist/src/datasets/florentine-families/index.d.ts +11 -0
- package/dist/src/datasets/florentine-families/index.d.ts.map +1 -0
- package/dist/src/datasets/florentine-families/index.js +14 -0
- package/dist/src/datasets/florentine-families/index.js.map +1 -0
- package/dist/src/datasets/florentine-families/meta.d.ts +4 -0
- package/dist/src/datasets/florentine-families/meta.d.ts.map +1 -0
- package/dist/src/datasets/florentine-families/meta.js +17 -0
- package/dist/src/datasets/florentine-families/meta.js.map +1 -0
- package/dist/src/datasets/football/data.d.ts +3 -0
- package/dist/src/datasets/football/data.d.ts.map +1 -0
- package/dist/src/datasets/football/data.js +210 -0
- package/dist/src/datasets/football/data.js.map +1 -0
- package/dist/src/datasets/football/index.d.ts +11 -0
- package/dist/src/datasets/football/index.d.ts.map +1 -0
- package/dist/src/datasets/football/index.js +14 -0
- package/dist/src/datasets/football/index.js.map +1 -0
- package/dist/src/datasets/football/meta.d.ts +4 -0
- package/dist/src/datasets/football/meta.d.ts.map +1 -0
- package/dist/src/datasets/football/meta.js +17 -0
- package/dist/src/datasets/football/meta.js.map +1 -0
- package/dist/src/datasets/hosted.d.ts +7 -0
- package/dist/src/datasets/hosted.d.ts.map +1 -0
- package/dist/src/datasets/hosted.js +80 -0
- package/dist/src/datasets/hosted.js.map +1 -0
- package/dist/src/datasets/karate/data.d.ts +3 -0
- package/dist/src/datasets/karate/data.d.ts.map +1 -0
- package/dist/src/datasets/karate/data.js +30 -0
- package/dist/src/datasets/karate/data.js.map +1 -0
- package/dist/src/datasets/karate/index.d.ts +11 -0
- package/dist/src/datasets/karate/index.d.ts.map +1 -0
- package/dist/src/datasets/karate/index.js +14 -0
- package/dist/src/datasets/karate/index.js.map +1 -0
- package/dist/src/datasets/karate/meta.d.ts +4 -0
- package/dist/src/datasets/karate/meta.d.ts.map +1 -0
- package/dist/src/datasets/karate/meta.js +22 -0
- package/dist/src/datasets/karate/meta.js.map +1 -0
- package/dist/src/datasets/knuth-miles/data.d.ts +3 -0
- package/dist/src/datasets/knuth-miles/data.d.ts.map +1 -0
- package/dist/src/datasets/knuth-miles/data.js +1203 -0
- package/dist/src/datasets/knuth-miles/data.js.map +1 -0
- package/dist/src/datasets/knuth-miles/index.d.ts +11 -0
- package/dist/src/datasets/knuth-miles/index.d.ts.map +1 -0
- package/dist/src/datasets/knuth-miles/index.js +14 -0
- package/dist/src/datasets/knuth-miles/index.js.map +1 -0
- package/dist/src/datasets/knuth-miles/meta.d.ts +4 -0
- package/dist/src/datasets/knuth-miles/meta.d.ts.map +1 -0
- package/dist/src/datasets/knuth-miles/meta.js +26 -0
- package/dist/src/datasets/knuth-miles/meta.js.map +1 -0
- package/dist/src/datasets/les-miserables/data.d.ts +3 -0
- package/dist/src/datasets/les-miserables/data.d.ts.map +1 -0
- package/dist/src/datasets/les-miserables/data.js +117 -0
- package/dist/src/datasets/les-miserables/data.js.map +1 -0
- package/dist/src/datasets/les-miserables/index.d.ts +11 -0
- package/dist/src/datasets/les-miserables/index.d.ts.map +1 -0
- package/dist/src/datasets/les-miserables/index.js +14 -0
- package/dist/src/datasets/les-miserables/index.js.map +1 -0
- package/dist/src/datasets/les-miserables/meta.d.ts +4 -0
- package/dist/src/datasets/les-miserables/meta.d.ts.map +1 -0
- package/dist/src/datasets/les-miserables/meta.js +21 -0
- package/dist/src/datasets/les-miserables/meta.js.map +1 -0
- package/dist/src/datasets/openflights/data.d.ts +3 -0
- package/dist/src/datasets/openflights/data.d.ts.map +1 -0
- package/dist/src/datasets/openflights/data.js +15457 -0
- package/dist/src/datasets/openflights/data.js.map +1 -0
- package/dist/src/datasets/openflights/index.d.ts +11 -0
- package/dist/src/datasets/openflights/index.d.ts.map +1 -0
- package/dist/src/datasets/openflights/index.js +14 -0
- package/dist/src/datasets/openflights/index.js.map +1 -0
- package/dist/src/datasets/openflights/meta.d.ts +4 -0
- package/dist/src/datasets/openflights/meta.d.ts.map +1 -0
- package/dist/src/datasets/openflights/meta.js +28 -0
- package/dist/src/datasets/openflights/meta.js.map +1 -0
- package/dist/src/datasets/political-blogs/data.d.ts +3 -0
- package/dist/src/datasets/political-blogs/data.d.ts.map +1 -0
- package/dist/src/datasets/political-blogs/data.js +4857 -0
- package/dist/src/datasets/political-blogs/data.js.map +1 -0
- package/dist/src/datasets/political-blogs/index.d.ts +11 -0
- package/dist/src/datasets/political-blogs/index.d.ts.map +1 -0
- package/dist/src/datasets/political-blogs/index.js +14 -0
- package/dist/src/datasets/political-blogs/index.js.map +1 -0
- package/dist/src/datasets/political-blogs/meta.d.ts +4 -0
- package/dist/src/datasets/political-blogs/meta.d.ts.map +1 -0
- package/dist/src/datasets/political-blogs/meta.js +26 -0
- package/dist/src/datasets/political-blogs/meta.js.map +1 -0
- package/dist/src/datasets/political-books/data.d.ts +3 -0
- package/dist/src/datasets/political-books/data.d.ts.map +1 -0
- package/dist/src/datasets/political-books/data.js +165 -0
- package/dist/src/datasets/political-books/data.js.map +1 -0
- package/dist/src/datasets/political-books/index.d.ts +11 -0
- package/dist/src/datasets/political-books/index.d.ts.map +1 -0
- package/dist/src/datasets/political-books/index.js +14 -0
- package/dist/src/datasets/political-books/index.js.map +1 -0
- package/dist/src/datasets/political-books/meta.d.ts +4 -0
- package/dist/src/datasets/political-books/meta.d.ts.map +1 -0
- package/dist/src/datasets/political-books/meta.js +20 -0
- package/dist/src/datasets/political-books/meta.js.map +1 -0
- package/dist/src/datasets/remote.d.ts +27 -0
- package/dist/src/datasets/remote.d.ts.map +1 -0
- package/dist/src/datasets/remote.js +30 -0
- package/dist/src/datasets/remote.js.map +1 -0
- package/dist/src/element.d.ts +20 -0
- package/dist/src/element.d.ts.map +1 -0
- package/dist/src/element.js +27 -0
- package/dist/src/element.js.map +1 -0
- package/dist/src/generators/bipartite.d.ts +51 -0
- package/dist/src/generators/bipartite.d.ts.map +1 -0
- package/dist/src/generators/bipartite.js +99 -0
- package/dist/src/generators/bipartite.js.map +1 -0
- package/dist/src/generators/block-model.d.ts +75 -0
- package/dist/src/generators/block-model.d.ts.map +1 -0
- package/dist/src/generators/block-model.js +116 -0
- package/dist/src/generators/block-model.js.map +1 -0
- package/dist/src/generators/classic.d.ts +208 -0
- package/dist/src/generators/classic.d.ts.map +1 -0
- package/dist/src/generators/classic.js +437 -0
- package/dist/src/generators/classic.js.map +1 -0
- package/dist/src/generators/degree-sequence.d.ts +273 -0
- package/dist/src/generators/degree-sequence.d.ts.map +1 -0
- package/dist/src/generators/degree-sequence.js +646 -0
- package/dist/src/generators/degree-sequence.js.map +1 -0
- package/dist/src/generators/directed.d.ts +141 -0
- package/dist/src/generators/directed.d.ts.map +1 -0
- package/dist/src/generators/directed.js +329 -0
- package/dist/src/generators/directed.js.map +1 -0
- package/dist/src/generators/erdos-renyi.d.ts +58 -0
- package/dist/src/generators/erdos-renyi.d.ts.map +1 -0
- package/dist/src/generators/erdos-renyi.js +154 -0
- package/dist/src/generators/erdos-renyi.js.map +1 -0
- package/dist/src/generators/flow.d.ts +153 -0
- package/dist/src/generators/flow.d.ts.map +1 -0
- package/dist/src/generators/flow.js +346 -0
- package/dist/src/generators/flow.js.map +1 -0
- package/dist/src/generators/geometric.d.ts +133 -0
- package/dist/src/generators/geometric.d.ts.map +1 -0
- package/dist/src/generators/geometric.js +495 -0
- package/dist/src/generators/geometric.js.map +1 -0
- package/dist/src/generators/growth.d.ts +187 -0
- package/dist/src/generators/growth.d.ts.map +1 -0
- package/dist/src/generators/growth.js +445 -0
- package/dist/src/generators/growth.js.map +1 -0
- package/dist/src/generators/hyperbolic.d.ts +84 -0
- package/dist/src/generators/hyperbolic.d.ts.map +1 -0
- package/dist/src/generators/hyperbolic.js +153 -0
- package/dist/src/generators/hyperbolic.js.map +1 -0
- package/dist/src/generators/index.d.ts +25 -0
- package/dist/src/generators/index.d.ts.map +1 -0
- package/dist/src/generators/index.js +24 -0
- package/dist/src/generators/index.js.map +1 -0
- package/dist/src/generators/lattices.d.ts +87 -0
- package/dist/src/generators/lattices.d.ts.map +1 -0
- package/dist/src/generators/lattices.js +237 -0
- package/dist/src/generators/lattices.js.map +1 -0
- package/dist/src/generators/lfr.d.ts +55 -0
- package/dist/src/generators/lfr.d.ts.map +1 -0
- package/dist/src/generators/lfr.js +178 -0
- package/dist/src/generators/lfr.js.map +1 -0
- package/dist/src/generators/named.d.ts +23 -0
- package/dist/src/generators/named.d.ts.map +1 -0
- package/dist/src/generators/named.js +191 -0
- package/dist/src/generators/named.js.map +1 -0
- package/dist/src/generators/preferential-attachment.d.ts +66 -0
- package/dist/src/generators/preferential-attachment.d.ts.map +1 -0
- package/dist/src/generators/preferential-attachment.js +157 -0
- package/dist/src/generators/preferential-attachment.js.map +1 -0
- package/dist/src/generators/trees-and-dags.d.ts +53 -0
- package/dist/src/generators/trees-and-dags.d.ts.map +1 -0
- package/dist/src/generators/trees-and-dags.js +108 -0
- package/dist/src/generators/trees-and-dags.js.map +1 -0
- package/dist/src/generators/util.d.ts +84 -0
- package/dist/src/generators/util.d.ts.map +1 -0
- package/dist/src/generators/util.js +138 -0
- package/dist/src/generators/util.js.map +1 -0
- package/dist/src/generators/variants.d.ts +85 -0
- package/dist/src/generators/variants.d.ts.map +1 -0
- package/dist/src/generators/variants.js +215 -0
- package/dist/src/generators/variants.js.map +1 -0
- package/dist/src/generators/weights.d.ts +65 -0
- package/dist/src/generators/weights.d.ts.map +1 -0
- package/dist/src/generators/weights.js +135 -0
- package/dist/src/generators/weights.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/random/exp.d.ts +23 -0
- package/dist/src/random/exp.d.ts.map +1 -0
- package/dist/src/random/exp.js +101 -0
- package/dist/src/random/exp.js.map +1 -0
- package/dist/src/random/log.d.ts +19 -0
- package/dist/src/random/log.d.ts.map +1 -0
- package/dist/src/random/log.js +83 -0
- package/dist/src/random/log.js.map +1 -0
- package/dist/src/random/stream.d.ts +93 -0
- package/dist/src/random/stream.d.ts.map +1 -0
- package/dist/src/random/stream.js +154 -0
- package/dist/src/random/stream.js.map +1 -0
- package/dist/src/random/threefry.d.ts +19 -0
- package/dist/src/random/threefry.d.ts.map +1 -0
- package/dist/src/random/threefry.js +43 -0
- package/dist/src/random/threefry.js.map +1 -0
- package/dist/src/random/trig.d.ts +16 -0
- package/dist/src/random/trig.d.ts.map +1 -0
- package/dist/src/random/trig.js +78 -0
- package/dist/src/random/trig.js.map +1 -0
- package/dist/src/types.d.ts +28 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +102 -5
- package/src/datasets/build.ts +140 -0
- package/src/datasets/catalog.ts +35 -0
- package/src/datasets/celegans-neural/data.ts +575 -0
- package/src/datasets/celegans-neural/index.ts +17 -0
- package/src/datasets/celegans-neural/meta.ts +25 -0
- package/src/datasets/contiguous-usa/data.ts +164 -0
- package/src/datasets/contiguous-usa/index.ts +17 -0
- package/src/datasets/contiguous-usa/meta.ts +31 -0
- package/src/datasets/davis-southern-women/data.ts +59 -0
- package/src/datasets/davis-southern-women/index.ts +17 -0
- package/src/datasets/davis-southern-women/meta.ts +20 -0
- package/src/datasets/dolphins/data.ts +95 -0
- package/src/datasets/dolphins/index.ts +17 -0
- package/src/datasets/dolphins/meta.ts +21 -0
- package/src/datasets/florentine-families/data.ts +33 -0
- package/src/datasets/florentine-families/index.ts +17 -0
- package/src/datasets/florentine-families/meta.ts +20 -0
- package/src/datasets/football/data.ts +212 -0
- package/src/datasets/football/index.ts +17 -0
- package/src/datasets/football/meta.ts +20 -0
- package/src/datasets/hosted.ts +90 -0
- package/src/datasets/karate/data.ts +32 -0
- package/src/datasets/karate/index.ts +17 -0
- package/src/datasets/karate/meta.ts +25 -0
- package/src/datasets/knuth-miles/data.ts +1205 -0
- package/src/datasets/knuth-miles/index.ts +17 -0
- package/src/datasets/knuth-miles/meta.ts +29 -0
- package/src/datasets/les-miserables/data.ts +119 -0
- package/src/datasets/les-miserables/index.ts +17 -0
- package/src/datasets/les-miserables/meta.ts +24 -0
- package/src/datasets/openflights/data.ts +15459 -0
- package/src/datasets/openflights/index.ts +17 -0
- package/src/datasets/openflights/meta.ts +31 -0
- package/src/datasets/political-blogs/data.ts +4859 -0
- package/src/datasets/political-blogs/index.ts +17 -0
- package/src/datasets/political-blogs/meta.ts +30 -0
- package/src/datasets/political-books/data.ts +167 -0
- package/src/datasets/political-books/index.ts +17 -0
- package/src/datasets/political-books/meta.ts +23 -0
- package/src/datasets/remote.ts +42 -0
- package/src/element.ts +41 -0
- package/src/generators/bipartite.ts +127 -0
- package/src/generators/block-model.ts +160 -0
- package/src/generators/classic.ts +461 -0
- package/src/generators/degree-sequence.ts +807 -0
- package/src/generators/directed.ts +418 -0
- package/src/generators/erdos-renyi.ts +188 -0
- package/src/generators/flow.ts +443 -0
- package/src/generators/geometric.ts +591 -0
- package/src/generators/growth.ts +532 -0
- package/src/generators/hyperbolic.ts +193 -0
- package/src/generators/index.ts +133 -0
- package/src/generators/lattices.ts +290 -0
- package/src/generators/lfr.ts +227 -0
- package/src/generators/named.ts +198 -0
- package/src/generators/preferential-attachment.ts +188 -0
- package/src/generators/trees-and-dags.ts +139 -0
- package/src/generators/util.ts +167 -0
- package/src/generators/variants.ts +253 -0
- package/src/generators/weights.ts +177 -0
- package/src/index.ts +12 -0
- package/src/random/exp.ts +104 -0
- package/src/random/log.ts +86 -0
- package/src/random/stream.ts +169 -0
- package/src/random/threefry.ts +45 -0
- package/src/random/trig.ts +83 -0
- package/src/types.ts +28 -0
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geometric random graphs: nodes are random points, edges depend on the distance between them.
|
|
3
|
+
* Every generator emits its points as Float64Array node columns `x`, `y` (and `z` in 3D), so the
|
|
4
|
+
* `weights: { kind: "euclidean" }` option and a fixed layout can use them.
|
|
5
|
+
*
|
|
6
|
+
* Node i's point always comes from its own stream block i, so the points do not depend on n or on
|
|
7
|
+
* how the work is split. Edge decisions compare squared distances (no square root).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { detLog } from "../random/log.js";
|
|
11
|
+
import { RandomStream, resolveSeed } from "../random/stream.js";
|
|
12
|
+
import { type SampleGraph } from "../types.js";
|
|
13
|
+
import { checkEdgeCount, checkInt, checkProbability, EdgeBuffer, toGraph } from "./util.js";
|
|
14
|
+
import { applyWeights, type WeightOptions } from "./weights.js";
|
|
15
|
+
|
|
16
|
+
/** L of the Waxman model: the diagonal of the unit square, the largest possible distance. */
|
|
17
|
+
const WAXMAN_L = Math.SQRT2;
|
|
18
|
+
|
|
19
|
+
/** The largest expected number of Waxman candidate draws, beta n^2 / 2. */
|
|
20
|
+
const WAXMAN_MAX_DRAWS = 5e8;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Throw unless `dimension` is 2 or 3.
|
|
24
|
+
* @param dimension - the option value
|
|
25
|
+
* @returns the dimension
|
|
26
|
+
*/
|
|
27
|
+
function checkDimension(dimension: number | undefined): 2 | 3 {
|
|
28
|
+
const d = dimension ?? 2;
|
|
29
|
+
if (d !== 2 && d !== 3) {
|
|
30
|
+
throw new RangeError(`dimension must be 2 or 3, got ${String(d)}`);
|
|
31
|
+
}
|
|
32
|
+
return d;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Throw unless `value` is a finite number >= 0 (or > 0 when `positive`).
|
|
37
|
+
* @param name - the option name
|
|
38
|
+
* @param value - the value
|
|
39
|
+
* @param positive - whether 0 is excluded
|
|
40
|
+
*/
|
|
41
|
+
function checkNonNegative(name: string, value: number, positive = false): void {
|
|
42
|
+
if (!(Number.isFinite(value) && (positive ? value > 0 : value >= 0))) {
|
|
43
|
+
throw new RangeError(`${name} must be a finite number ${positive ? ">" : ">="} 0, got ${String(value)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Uniform points in [0, 1)^dimension: point i takes `dimension` nextFloat() draws (x, then y, then
|
|
49
|
+
* z) from stream (seed, domain, i).
|
|
50
|
+
* @param seed - the seed
|
|
51
|
+
* @param domain - the points' stream domain
|
|
52
|
+
* @param n - the point count
|
|
53
|
+
* @param dimension - 2 or 3
|
|
54
|
+
* @returns one column per axis
|
|
55
|
+
*/
|
|
56
|
+
function uniformPoints(seed: number, domain: string, n: number, dimension: number): Float64Array<ArrayBuffer>[] {
|
|
57
|
+
const axes = Array.from({ length: dimension }, () => new Float64Array(n));
|
|
58
|
+
const stream = new RandomStream(seed, domain, 0);
|
|
59
|
+
for (let i = 0; i < n; i++) {
|
|
60
|
+
stream.reset(i);
|
|
61
|
+
for (const axis of axes) {
|
|
62
|
+
axis[i] = stream.nextFloat();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return axes;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The position columns x, y (and z) of a graph.
|
|
70
|
+
* @param axes - the coordinate columns
|
|
71
|
+
* @returns the node columns
|
|
72
|
+
*/
|
|
73
|
+
function positionColumns(axes: Float64Array<ArrayBuffer>[]): Record<string, Float64Array<ArrayBuffer>> {
|
|
74
|
+
const names = ["x", "y", "z"];
|
|
75
|
+
return Object.fromEntries(axes.map((axis, a) => [names[a], axis]));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The largest g >= 1 with g^dimension <= target.
|
|
80
|
+
* @param target - the cell budget
|
|
81
|
+
* @param dimension - 2 or 3
|
|
82
|
+
* @returns cells per axis
|
|
83
|
+
*/
|
|
84
|
+
function cellsPerAxis(target: number, dimension: number): number {
|
|
85
|
+
let g = 1;
|
|
86
|
+
while ((g + 1) ** dimension <= target) {
|
|
87
|
+
g++;
|
|
88
|
+
}
|
|
89
|
+
return g;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Points bucketed into a uniform grid of g^dimension cells by a counting sort. */
|
|
93
|
+
interface CellGrid {
|
|
94
|
+
/** Cells per axis. */
|
|
95
|
+
readonly g: number;
|
|
96
|
+
/** The low corner of the grid on every axis. */
|
|
97
|
+
readonly lo: readonly number[];
|
|
98
|
+
/** The cell side on every axis. */
|
|
99
|
+
readonly side: readonly number[];
|
|
100
|
+
/** start[c] .. start[c + 1] indexes `items` for cell c. */
|
|
101
|
+
readonly start: Uint32Array;
|
|
102
|
+
/** Point indices grouped by cell, ascending inside each cell. */
|
|
103
|
+
readonly items: Uint32Array;
|
|
104
|
+
/** The cell coordinate of every point on every axis. */
|
|
105
|
+
readonly cell: Uint32Array[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Bucket points into a g^dimension grid spanning [lo, hi] on each axis.
|
|
110
|
+
* @param axes - the coordinate columns
|
|
111
|
+
* @param g - cells per axis
|
|
112
|
+
* @param lo - the low corner per axis
|
|
113
|
+
* @param hi - the high corner per axis
|
|
114
|
+
* @returns the grid
|
|
115
|
+
*/
|
|
116
|
+
function buildGrid(axes: Float64Array[], g: number, lo: number[], hi: number[]): CellGrid {
|
|
117
|
+
const n = axes[0].length;
|
|
118
|
+
const side = lo.map((l, a) => (hi[a] - l) / g || 1);
|
|
119
|
+
const cell = axes.map(() => new Uint32Array(n));
|
|
120
|
+
const flat = new Uint32Array(n);
|
|
121
|
+
const start = new Uint32Array(g ** axes.length + 1);
|
|
122
|
+
for (let i = 0; i < n; i++) {
|
|
123
|
+
let c = 0;
|
|
124
|
+
for (let a = 0; a < axes.length; a++) {
|
|
125
|
+
const k = Math.min(g - 1, Math.floor((axes[a][i] - lo[a]) / side[a]));
|
|
126
|
+
cell[a][i] = k;
|
|
127
|
+
c = c * g + k;
|
|
128
|
+
}
|
|
129
|
+
flat[i] = c;
|
|
130
|
+
start[c + 1]++;
|
|
131
|
+
}
|
|
132
|
+
for (let c = 0; c + 1 < start.length; c++) {
|
|
133
|
+
start[c + 1] += start[c];
|
|
134
|
+
}
|
|
135
|
+
const fill = start.slice(0, -1);
|
|
136
|
+
const items = new Uint32Array(n);
|
|
137
|
+
for (let i = 0; i < n; i++) {
|
|
138
|
+
items[fill[flat[i]]++] = i;
|
|
139
|
+
}
|
|
140
|
+
return { g, lo, side, start, items, cell };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Options of {@link randomGeometricGraph}. */
|
|
144
|
+
export interface RandomGeometricOptions extends WeightOptions {
|
|
145
|
+
/** The node count, >= 0. */
|
|
146
|
+
n: number;
|
|
147
|
+
/** The connection radius, a finite number >= 0: u and v are joined iff their distance is <= radius. */
|
|
148
|
+
radius: number;
|
|
149
|
+
/** 2 (the unit square, default) or 3 (the unit cube). */
|
|
150
|
+
dimension?: 2 | 3 | undefined;
|
|
151
|
+
/** Measure distance on the torus (each axis wraps around), removing the boundary effect; default false. */
|
|
152
|
+
periodic?: boolean | undefined;
|
|
153
|
+
/** The seed, an integer in [0, 2^53); default 0. */
|
|
154
|
+
seed?: number | undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The random geometric graph (E. N. Gilbert, "Random plane networks", J. SIAM 9(4), 533-543, 1961,
|
|
159
|
+
* doi:10.1137/0109045; M. Penrose, Random Geometric Graphs, Oxford University Press, 2003): n
|
|
160
|
+
* uniform points in the unit square or cube, u and v joined iff their Euclidean distance (torus
|
|
161
|
+
* distance when `periodic`) is at most `radius`.
|
|
162
|
+
*
|
|
163
|
+
* Node i's point is x, y (, z) = one nextFloat() per axis from stream (seed, "rgg-points", i). The
|
|
164
|
+
* points are bucketed into a uniform grid of cells of side >= radius (at most about n cells) and
|
|
165
|
+
* each node checks the 3^dimension cells around its own. Edges are the pairs (u, v), u < v,
|
|
166
|
+
* ascending u then ascending v. Expected cost O(n + m). Node columns `x`, `y` (, `z`): Float64Array
|
|
167
|
+
* in [0, 1). The mean degree is about n pi r^2 in 2D and n 4/3 pi r^3 in 3D (less near the border
|
|
168
|
+
* unless periodic); connectivity needs r above about sqrt(ln n / (pi n)) in 2D.
|
|
169
|
+
* @param options - n, radius, dimension, periodic and seed
|
|
170
|
+
* @returns the undirected graph
|
|
171
|
+
*/
|
|
172
|
+
export function randomGeometricGraph(options: RandomGeometricOptions): SampleGraph {
|
|
173
|
+
const { n, radius } = options;
|
|
174
|
+
checkInt("n", n, 0);
|
|
175
|
+
checkNonNegative("radius", radius);
|
|
176
|
+
const dimension = checkDimension(options.dimension);
|
|
177
|
+
const periodic = options.periodic ?? false;
|
|
178
|
+
const seed = resolveSeed(options.seed);
|
|
179
|
+
const axes = uniformPoints(seed, "rgg-points", n, dimension);
|
|
180
|
+
|
|
181
|
+
const r2 = radius * radius;
|
|
182
|
+
// cells a hair wider than the radius, so a point rounded into the next cell is still covered
|
|
183
|
+
const g = Math.max(1, Math.min(Math.floor((1 - 1e-9) / radius), cellsPerAxis(Math.max(1, n), dimension)));
|
|
184
|
+
const grid = buildGrid(axes, g, new Array<number>(dimension).fill(0), new Array<number>(dimension).fill(1));
|
|
185
|
+
const volume = dimension === 2 ? Math.PI * r2 : (4 / 3) * Math.PI * r2 * radius;
|
|
186
|
+
const pairs = (n * (n - 1)) / 2;
|
|
187
|
+
const out = new EdgeBuffer(Math.ceil(Math.min(pairs, pairs * volume * 1.1, 1 << 24) + 16));
|
|
188
|
+
|
|
189
|
+
// the distinct neighbouring cell coordinates of cell k on one axis
|
|
190
|
+
const around = Array.from({ length: g }, (_, k) => {
|
|
191
|
+
const list = periodic ? [(k + g - 1) % g, k, (k + 1) % g] : [k - 1, k, k + 1].filter((c) => c >= 0 && c < g);
|
|
192
|
+
return [...new Set(list)];
|
|
193
|
+
});
|
|
194
|
+
let found = new Uint32Array(16);
|
|
195
|
+
for (let u = 0; u < n; u++) {
|
|
196
|
+
let count = 0;
|
|
197
|
+
const zCells = dimension === 3 ? around[grid.cell[2][u]] : [0];
|
|
198
|
+
for (const cx of around[grid.cell[0][u]]) {
|
|
199
|
+
for (const cy of around[grid.cell[1][u]]) {
|
|
200
|
+
for (const cz of zCells) {
|
|
201
|
+
const c = dimension === 3 ? (cx * g + cy) * g + cz : cx * g + cy;
|
|
202
|
+
for (let t = grid.start[c]; t < grid.start[c + 1]; t++) {
|
|
203
|
+
const v = grid.items[t];
|
|
204
|
+
if (v <= u) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
let d2 = 0;
|
|
208
|
+
for (const axis of axes) {
|
|
209
|
+
let d = Math.abs(axis[u] - axis[v]);
|
|
210
|
+
if (periodic && d > 0.5) {
|
|
211
|
+
d = 1 - d;
|
|
212
|
+
}
|
|
213
|
+
d2 += d * d;
|
|
214
|
+
}
|
|
215
|
+
if (d2 <= r2) {
|
|
216
|
+
if (count === found.length) {
|
|
217
|
+
const grown = new Uint32Array(count * 2);
|
|
218
|
+
grown.set(found);
|
|
219
|
+
found = grown;
|
|
220
|
+
}
|
|
221
|
+
found[count++] = v;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const row = found.subarray(0, count).sort();
|
|
228
|
+
for (const v of row) {
|
|
229
|
+
out.push(u, v);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return applyWeights(toGraph(n, out, false, positionColumns(axes)), options);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Options of {@link waxmanGraph}. */
|
|
236
|
+
export interface WaxmanOptions extends WeightOptions {
|
|
237
|
+
/** The node count, >= 0; beta n^2 / 2 must stay below 5e8. */
|
|
238
|
+
n: number;
|
|
239
|
+
/** The distance scale, a finite number > 0: larger alpha makes long edges likelier. */
|
|
240
|
+
alpha: number;
|
|
241
|
+
/** The edge probability at distance 0, in [0, 1]. */
|
|
242
|
+
beta: number;
|
|
243
|
+
/** The seed, an integer in [0, 2^53); default 0. */
|
|
244
|
+
seed?: number | undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** The checked and precomputed form of {@link WaxmanOptions}. */
|
|
248
|
+
interface WaxmanPlan {
|
|
249
|
+
readonly n: number;
|
|
250
|
+
readonly seed: number;
|
|
251
|
+
readonly beta: number;
|
|
252
|
+
readonly logQ: number;
|
|
253
|
+
/** alpha L. */
|
|
254
|
+
readonly scale: number;
|
|
255
|
+
readonly x: Float64Array<ArrayBuffer>;
|
|
256
|
+
readonly y: Float64Array<ArrayBuffer>;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Check the Waxman options and draw the points.
|
|
261
|
+
* @param options - the options
|
|
262
|
+
* @returns the plan
|
|
263
|
+
*/
|
|
264
|
+
export function planWaxman(options: WaxmanOptions): WaxmanPlan {
|
|
265
|
+
const { n, alpha, beta } = options;
|
|
266
|
+
checkInt("n", n, 0);
|
|
267
|
+
checkNonNegative("alpha", alpha, true);
|
|
268
|
+
checkProbability("beta", beta);
|
|
269
|
+
const seed = resolveSeed(options.seed);
|
|
270
|
+
if ((beta * n * n) / 2 > WAXMAN_MAX_DRAWS) {
|
|
271
|
+
throw new RangeError(
|
|
272
|
+
`waxmanGraph: beta n^2 / 2 = ${(beta * n * n) / 2} candidate pairs, above the limit of ${WAXMAN_MAX_DRAWS}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
const [x, y] = uniformPoints(seed, "waxman-points", n, 2);
|
|
276
|
+
return { n, seed, beta, logQ: detLog(1 - beta), scale: alpha * WAXMAN_L, x, y };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Rows [start, end) of the Waxman graph: row u draws from stream (seed, "waxman", u), walks the
|
|
281
|
+
* candidates v > u by geometric skipping at probability beta (one draw per skip), and keeps a
|
|
282
|
+
* candidate with a second draw t iff d^2 < (alpha L ln t)^2, which is t < exp(-d / (alpha L)) with
|
|
283
|
+
* no exp. Edges ascend in v. Any split of [0, n) into consecutive ranges gives the whole graph.
|
|
284
|
+
* @param plan - the checked options and points
|
|
285
|
+
* @param start - the first row
|
|
286
|
+
* @param end - one past the last row
|
|
287
|
+
* @param out - receives the edges
|
|
288
|
+
*/
|
|
289
|
+
export function waxmanRows(plan: WaxmanPlan, start: number, end: number, out: EdgeBuffer): void {
|
|
290
|
+
const { n, beta, logQ, scale, x, y } = plan;
|
|
291
|
+
if (beta === 0) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const stream = new RandomStream(plan.seed, "waxman", 0);
|
|
295
|
+
const skip = (): number => (beta === 1 ? 0 : stream.nextSkip(logQ));
|
|
296
|
+
for (let u = start; u < end; u++) {
|
|
297
|
+
stream.reset(u);
|
|
298
|
+
for (let v = u + 1 + skip(); v < n; v += 1 + skip()) {
|
|
299
|
+
const dx = x[u] - x[v];
|
|
300
|
+
const dy = y[u] - y[v];
|
|
301
|
+
const bound = scale * detLog(stream.nextFloat());
|
|
302
|
+
if (dx * dx + dy * dy < bound * bound) {
|
|
303
|
+
out.push(u, v);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The Waxman graph (B. M. Waxman, "Routing of multipoint connections", IEEE J. Selected Areas in
|
|
311
|
+
* Communications 6(9), 1617-1622, 1988): n uniform points in the unit square, each pair joined
|
|
312
|
+
* independently with probability beta exp(-d / (alpha L)).
|
|
313
|
+
*
|
|
314
|
+
* L is fixed at sqrt(2), the diagonal of the unit square (networkx uses the largest distance
|
|
315
|
+
* between the drawn points instead, so the two differ slightly). Node i's point is x, y = two
|
|
316
|
+
* nextFloat() draws from stream (seed, "waxman-points", i); the pairs are drawn by
|
|
317
|
+
* {@link waxmanRows}. Edges are the pairs (u, v), u < v, ascending u then v. Every pair has a
|
|
318
|
+
* non-zero probability, so the cost is O(n + beta n^2 / 2) draws; above 5e8 expected draws it
|
|
319
|
+
* throws a RangeError (n about 31,600 at beta 1). Node columns `x`, `y`: Float64Array in [0, 1).
|
|
320
|
+
* @param options - n, alpha, beta and seed
|
|
321
|
+
* @returns the undirected graph
|
|
322
|
+
*/
|
|
323
|
+
export function waxmanGraph(options: WaxmanOptions): SampleGraph {
|
|
324
|
+
const plan = planWaxman(options);
|
|
325
|
+
const out = new EdgeBuffer(Math.min(Math.ceil((plan.beta * plan.n * plan.n) / 8), 1 << 24) + 16);
|
|
326
|
+
waxmanRows(plan, 0, plan.n, out);
|
|
327
|
+
return applyWeights(toGraph(plan.n, out, false, positionColumns([plan.x, plan.y])), options);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Options of {@link knnGraph}. */
|
|
331
|
+
export interface KnnOptions extends WeightOptions {
|
|
332
|
+
/** The node count, >= 0. */
|
|
333
|
+
n: number;
|
|
334
|
+
/** The neighbours per node, in [0, n - 1]. */
|
|
335
|
+
k: number;
|
|
336
|
+
/** 2 (default) or 3. */
|
|
337
|
+
dimension?: 2 | 3 | undefined;
|
|
338
|
+
/** Arcs u -> each of u's k nearest (default true), or with false the undirected union without duplicates. */
|
|
339
|
+
directed?: boolean | undefined;
|
|
340
|
+
/** Draw the points from a mixture of this many Gaussian clusters, >= 1; default uniform points. */
|
|
341
|
+
clusters?: number | undefined;
|
|
342
|
+
/** The standard deviation of every cluster on each axis, a finite number >= 0; default 0.05. Needs `clusters`. */
|
|
343
|
+
spread?: number | undefined;
|
|
344
|
+
/** The seed, an integer in [0, 2^53); default 0. */
|
|
345
|
+
seed?: number | undefined;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Gaussian mixture points: cluster c's centre is `dimension` nextFloat() draws from stream
|
|
350
|
+
* (seed, "knn-centres", c); point i draws its cluster with nextBelow(clusters) and then standard
|
|
351
|
+
* normal offsets by the Marsaglia polar method (pairs u, v = 2 nextFloat() - 1 until
|
|
352
|
+
* 0 < s = u^2 + v^2 < 1, then u sqrt(-2 ln s / s) and v sqrt(-2 ln s / s)), all from stream
|
|
353
|
+
* (seed, "knn-points", i).
|
|
354
|
+
* @param seed - the seed
|
|
355
|
+
* @param n - the point count
|
|
356
|
+
* @param dimension - 2 or 3
|
|
357
|
+
* @param clusters - the cluster count
|
|
358
|
+
* @param spread - the standard deviation
|
|
359
|
+
* @returns the coordinate columns and the cluster of every point
|
|
360
|
+
*/
|
|
361
|
+
function mixturePoints(
|
|
362
|
+
seed: number,
|
|
363
|
+
n: number,
|
|
364
|
+
dimension: number,
|
|
365
|
+
clusters: number,
|
|
366
|
+
spread: number,
|
|
367
|
+
): { axes: Float64Array<ArrayBuffer>[]; community: Uint32Array<ArrayBuffer> } {
|
|
368
|
+
const centres = uniformPoints(seed, "knn-centres", clusters, dimension);
|
|
369
|
+
const axes = Array.from({ length: dimension }, () => new Float64Array(n));
|
|
370
|
+
const community = new Uint32Array(n);
|
|
371
|
+
const stream = new RandomStream(seed, "knn-points", 0);
|
|
372
|
+
const normals = new Float64Array(4);
|
|
373
|
+
for (let i = 0; i < n; i++) {
|
|
374
|
+
stream.reset(i);
|
|
375
|
+
const c = stream.nextBelow(clusters);
|
|
376
|
+
community[i] = c;
|
|
377
|
+
for (let j = 0; j < dimension; j += 2) {
|
|
378
|
+
let u: number;
|
|
379
|
+
let v: number;
|
|
380
|
+
let s: number;
|
|
381
|
+
do {
|
|
382
|
+
u = 2 * stream.nextFloat() - 1;
|
|
383
|
+
v = 2 * stream.nextFloat() - 1;
|
|
384
|
+
s = u * u + v * v;
|
|
385
|
+
} while (s >= 1 || s === 0);
|
|
386
|
+
const m = Math.sqrt((-2 * detLog(s)) / s);
|
|
387
|
+
normals[j] = u * m;
|
|
388
|
+
normals[j + 1] = v * m;
|
|
389
|
+
}
|
|
390
|
+
for (let a = 0; a < dimension; a++) {
|
|
391
|
+
axes[a][i] = centres[a][c] + spread * normals[a];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return { axes, community };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* The k nearest neighbours of every point, exact, by an expanding ring search over a cell grid.
|
|
399
|
+
* @param axes - the coordinate columns
|
|
400
|
+
* @param k - neighbours per point
|
|
401
|
+
* @returns row u's neighbours at [u k, (u + 1) k), ascending by index
|
|
402
|
+
*/
|
|
403
|
+
function nearestNeighbours(axes: Float64Array[], k: number): Uint32Array<ArrayBuffer> {
|
|
404
|
+
const n = axes[0].length;
|
|
405
|
+
const dimension = axes.length;
|
|
406
|
+
const result = new Uint32Array(n * k);
|
|
407
|
+
if (k === 0) {
|
|
408
|
+
return result;
|
|
409
|
+
}
|
|
410
|
+
const lo = axes.map((axis) => axis.reduce((a, b) => Math.min(a, b), Infinity));
|
|
411
|
+
const hi = axes.map((axis) => axis.reduce((a, b) => Math.max(a, b), -Infinity));
|
|
412
|
+
const g = cellsPerAxis(Math.max(1, n / k), dimension);
|
|
413
|
+
const grid = buildGrid(axes, g, lo, hi);
|
|
414
|
+
const bestD = new Float64Array(k);
|
|
415
|
+
const bestI = new Uint32Array(k);
|
|
416
|
+
// the absolute rounding slack of a cell assignment
|
|
417
|
+
const slack = 1e-12 * Math.max(1, ...lo.map(Math.abs), ...hi.map(Math.abs));
|
|
418
|
+
const cell = new Array<number>(dimension).fill(0);
|
|
419
|
+
const offset = new Array<number>(dimension).fill(0);
|
|
420
|
+
for (let u = 0; u < n; u++) {
|
|
421
|
+
let count = 0;
|
|
422
|
+
const consider = (v: number): void => {
|
|
423
|
+
let d2 = 0;
|
|
424
|
+
for (const axis of axes) {
|
|
425
|
+
const d = Math.abs(axis[u] - axis[v]);
|
|
426
|
+
d2 += d * d;
|
|
427
|
+
}
|
|
428
|
+
// insert (d2, v) into the sorted best list when it beats the worst kept
|
|
429
|
+
if (count === k && (d2 > bestD[k - 1] || (d2 === bestD[k - 1] && v > bestI[k - 1]))) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
let j = count < k ? count++ : k - 1;
|
|
433
|
+
while (j > 0 && (bestD[j - 1] > d2 || (bestD[j - 1] === d2 && bestI[j - 1] > v))) {
|
|
434
|
+
bestD[j] = bestD[j - 1];
|
|
435
|
+
bestI[j] = bestI[j - 1];
|
|
436
|
+
j--;
|
|
437
|
+
}
|
|
438
|
+
bestD[j] = d2;
|
|
439
|
+
bestI[j] = v;
|
|
440
|
+
};
|
|
441
|
+
for (let a = 0; a < dimension; a++) {
|
|
442
|
+
cell[a] = grid.cell[a][u];
|
|
443
|
+
}
|
|
444
|
+
// ponytail: ring r scans the whole (2r + 1)^dimension block and skips its inside, O(r^dimension)
|
|
445
|
+
// per ring; walk only the shell if large k or very uneven clusters make that measurable
|
|
446
|
+
for (let r = 0; ; r++) {
|
|
447
|
+
let covered = true;
|
|
448
|
+
for (let a = 0; a < dimension; a++) {
|
|
449
|
+
offset[a] = -r;
|
|
450
|
+
if (cell[a] - r > 0 || cell[a] + r < g - 1) {
|
|
451
|
+
covered = false;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
for (;;) {
|
|
455
|
+
let chebyshev = 0;
|
|
456
|
+
let inside = true;
|
|
457
|
+
let c = 0;
|
|
458
|
+
for (let a = 0; a < dimension; a++) {
|
|
459
|
+
const k2 = cell[a] + offset[a];
|
|
460
|
+
if (k2 < 0 || k2 >= g) {
|
|
461
|
+
inside = false;
|
|
462
|
+
}
|
|
463
|
+
chebyshev = Math.max(chebyshev, Math.abs(offset[a]));
|
|
464
|
+
c = c * g + k2;
|
|
465
|
+
}
|
|
466
|
+
if (inside && chebyshev === r) {
|
|
467
|
+
for (let t = grid.start[c]; t < grid.start[c + 1]; t++) {
|
|
468
|
+
const v = grid.items[t];
|
|
469
|
+
if (v !== u) {
|
|
470
|
+
consider(v);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
let a = dimension - 1;
|
|
475
|
+
while (a >= 0 && offset[a] === r) {
|
|
476
|
+
offset[a] = -r;
|
|
477
|
+
a--;
|
|
478
|
+
}
|
|
479
|
+
if (a < 0) {
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
offset[a]++;
|
|
483
|
+
}
|
|
484
|
+
if (covered) {
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
if (count === k) {
|
|
488
|
+
// every point outside the searched block is at least `gap` away
|
|
489
|
+
let gap = Infinity;
|
|
490
|
+
for (let a = 0; a < dimension; a++) {
|
|
491
|
+
const p = axes[a][u] - grid.lo[a];
|
|
492
|
+
gap = Math.min(gap, p - (cell[a] - r) * grid.side[a], (cell[a] + r + 1) * grid.side[a] - p);
|
|
493
|
+
}
|
|
494
|
+
// the margins cover the rounding of the cell assignment and of the squared distances, so
|
|
495
|
+
// a point outside the block can never tie or beat the k-th best
|
|
496
|
+
gap -= slack;
|
|
497
|
+
if (gap > 0 && bestD[k - 1] < gap * gap * (1 - 1e-9)) {
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const row = result.subarray(u * k, (u + 1) * k);
|
|
503
|
+
row.set(bestI);
|
|
504
|
+
row.sort();
|
|
505
|
+
}
|
|
506
|
+
return result;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* The k-nearest-neighbour graph of random points, the standard input of spectral clustering
|
|
511
|
+
* (U. von Luxburg, "A tutorial on spectral clustering", Statistics and Computing 17, 395-416, 2007,
|
|
512
|
+
* doi:10.1007/s11222-007-9033-z).
|
|
513
|
+
*
|
|
514
|
+
* Points: uniform in the unit square or cube (point i = nextFloat() per axis from stream
|
|
515
|
+
* (seed, "knn-points", i)), or with `clusters` a Gaussian mixture: cluster centres uniform in the
|
|
516
|
+
* unit cube from stream (seed, "knn-centres", c), point i picks its cluster uniformly and adds
|
|
517
|
+
* normal offsets of standard deviation `spread` (Marsaglia polar method) from stream
|
|
518
|
+
* (seed, "knn-points", i); mixture points may leave [0, 1). Each node's k nearest are exact,
|
|
519
|
+
* ties broken by the smaller index, found by an expanding ring search over a cell grid of about
|
|
520
|
+
* n / k cells: expected O(n k^2) for uniform points.
|
|
521
|
+
*
|
|
522
|
+
* Directed (default): the arcs u -> each of its k nearest, ascending u then ascending target.
|
|
523
|
+
* Undirected: the union of those pairs without duplicates as (u, v), u < v, ascending u then v.
|
|
524
|
+
* Node columns `x`, `y` (, `z`): Float64Array; with `clusters`, `community` (u32): the cluster.
|
|
525
|
+
* @param options - n, k, dimension, directed, clusters, spread and seed
|
|
526
|
+
* @returns the graph
|
|
527
|
+
*/
|
|
528
|
+
export function knnGraph(options: KnnOptions): SampleGraph {
|
|
529
|
+
const { n, k, clusters, spread } = options;
|
|
530
|
+
checkInt("n", n, 0);
|
|
531
|
+
checkInt("k", k, 0, Math.max(0, n - 1));
|
|
532
|
+
const dimension = checkDimension(options.dimension);
|
|
533
|
+
const directed = options.directed ?? true;
|
|
534
|
+
if (clusters === undefined && spread !== undefined) {
|
|
535
|
+
throw new RangeError("spread needs clusters");
|
|
536
|
+
}
|
|
537
|
+
const seed = resolveSeed(options.seed);
|
|
538
|
+
let axes: Float64Array<ArrayBuffer>[];
|
|
539
|
+
const columns: Record<string, Float64Array<ArrayBuffer> | Uint32Array<ArrayBuffer>> = {};
|
|
540
|
+
if (clusters === undefined) {
|
|
541
|
+
axes = uniformPoints(seed, "knn-points", n, dimension);
|
|
542
|
+
} else {
|
|
543
|
+
checkInt("clusters", clusters, 1);
|
|
544
|
+
const sd = spread ?? 0.05;
|
|
545
|
+
checkNonNegative("spread", sd);
|
|
546
|
+
let community: Uint32Array<ArrayBuffer>;
|
|
547
|
+
({ axes, community } = mixturePoints(seed, n, dimension, clusters, sd));
|
|
548
|
+
columns.community = community;
|
|
549
|
+
}
|
|
550
|
+
Object.assign(columns, positionColumns(axes));
|
|
551
|
+
const neighbours = nearestNeighbours(axes, k);
|
|
552
|
+
checkEdgeCount(n * k);
|
|
553
|
+
let out: EdgeBuffer;
|
|
554
|
+
if (directed) {
|
|
555
|
+
out = new EdgeBuffer(n * k);
|
|
556
|
+
for (let u = 0; u < n; u++) {
|
|
557
|
+
for (let j = u * k; j < (u + 1) * k; j++) {
|
|
558
|
+
out.push(u, neighbours[j]);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
// bucket the pairs (min, max) by their smaller end with a counting sort, then sort and dedupe each bucket
|
|
563
|
+
const start = new Uint32Array(n + 1);
|
|
564
|
+
for (let u = 0; u < n; u++) {
|
|
565
|
+
for (let j = u * k; j < (u + 1) * k; j++) {
|
|
566
|
+
start[Math.min(u, neighbours[j]) + 1]++;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
for (let u = 0; u < n; u++) {
|
|
570
|
+
start[u + 1] += start[u];
|
|
571
|
+
}
|
|
572
|
+
const fill = start.slice(0, -1);
|
|
573
|
+
const other = new Uint32Array(n * k);
|
|
574
|
+
for (let u = 0; u < n; u++) {
|
|
575
|
+
for (let j = u * k; j < (u + 1) * k; j++) {
|
|
576
|
+
const v = neighbours[j];
|
|
577
|
+
other[fill[Math.min(u, v)]++] = Math.max(u, v);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
out = new EdgeBuffer(n * k);
|
|
581
|
+
for (let u = 0; u < n; u++) {
|
|
582
|
+
const row = other.subarray(start[u], start[u + 1]).sort();
|
|
583
|
+
for (let j = 0; j < row.length; j++) {
|
|
584
|
+
if (j === 0 || row[j] !== row[j - 1]) {
|
|
585
|
+
out.push(u, row[j]);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return applyWeights(toGraph(n, out, directed, columns), options);
|
|
591
|
+
}
|