@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,4496 @@
|
|
|
1
|
+
import { MAX_COUNT } from "@graphty/graph-format";
|
|
2
|
+
const LN2_HI$1 = 0.6931471803691238;
|
|
3
|
+
const LN2_LO$1 = 19082149292705877e-26;
|
|
4
|
+
const TWO54 = 18014398509481984;
|
|
5
|
+
const LG1 = 0.6666666666666735;
|
|
6
|
+
const LG2 = 0.3999999999940942;
|
|
7
|
+
const LG3 = 0.2857142874366239;
|
|
8
|
+
const LG4 = 0.22222198432149784;
|
|
9
|
+
const LG5 = 0.1818357216161805;
|
|
10
|
+
const LG6 = 0.15313837699209373;
|
|
11
|
+
const LG7 = 0.14798198605116586;
|
|
12
|
+
const view$1 = new DataView(new ArrayBuffer(8));
|
|
13
|
+
function detLog(input) {
|
|
14
|
+
let x = input;
|
|
15
|
+
view$1.setFloat64(0, x, true);
|
|
16
|
+
let hx = view$1.getInt32(4, true);
|
|
17
|
+
const lx = view$1.getUint32(0, true);
|
|
18
|
+
let k = 0;
|
|
19
|
+
if (hx < 1048576) {
|
|
20
|
+
if ((hx & 2147483647 | lx) === 0) {
|
|
21
|
+
return -Infinity;
|
|
22
|
+
}
|
|
23
|
+
if (hx < 0) {
|
|
24
|
+
return NaN;
|
|
25
|
+
}
|
|
26
|
+
k -= 54;
|
|
27
|
+
x *= TWO54;
|
|
28
|
+
view$1.setFloat64(0, x, true);
|
|
29
|
+
hx = view$1.getInt32(4, true);
|
|
30
|
+
}
|
|
31
|
+
if (hx >= 2146435072) {
|
|
32
|
+
return x + x;
|
|
33
|
+
}
|
|
34
|
+
k += (hx >> 20) - 1023;
|
|
35
|
+
hx &= 1048575;
|
|
36
|
+
const i0 = hx + 614244 & 1048576;
|
|
37
|
+
view$1.setInt32(4, hx | i0 ^ 1072693248, true);
|
|
38
|
+
x = view$1.getFloat64(0, true);
|
|
39
|
+
k += i0 >> 20;
|
|
40
|
+
const f = x - 1;
|
|
41
|
+
if ((1048575 & 2 + hx) < 3) {
|
|
42
|
+
if (f === 0) {
|
|
43
|
+
return k === 0 ? 0 : k * LN2_HI$1 + k * LN2_LO$1;
|
|
44
|
+
}
|
|
45
|
+
const r = f * f * (0.5 - 0.3333333333333333 * f);
|
|
46
|
+
return k === 0 ? f - r : k * LN2_HI$1 - (r - k * LN2_LO$1 - f);
|
|
47
|
+
}
|
|
48
|
+
const s = f / (2 + f);
|
|
49
|
+
const dk = k;
|
|
50
|
+
const z = s * s;
|
|
51
|
+
let i = hx - 398458;
|
|
52
|
+
const w = z * z;
|
|
53
|
+
const j = 440401 - hx;
|
|
54
|
+
const t1 = w * (LG2 + w * (LG4 + w * LG6));
|
|
55
|
+
const t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7)));
|
|
56
|
+
i |= j;
|
|
57
|
+
const R = t2 + t1;
|
|
58
|
+
if (i > 0) {
|
|
59
|
+
const hfsq = 0.5 * f * f;
|
|
60
|
+
return k === 0 ? f - (hfsq - s * (hfsq + R)) : dk * LN2_HI$1 - (hfsq - (s * (hfsq + R) + dk * LN2_LO$1) - f);
|
|
61
|
+
}
|
|
62
|
+
return k === 0 ? f - s * (f - R) : dk * LN2_HI$1 - (s * (f - R) - dk * LN2_LO$1 - f);
|
|
63
|
+
}
|
|
64
|
+
const ROTATIONS = [13, 15, 26, 6, 17, 29, 16, 24];
|
|
65
|
+
const KS_PARITY = 466688986;
|
|
66
|
+
function threefry2x32(k0, k1, c0, c1, out) {
|
|
67
|
+
const ks0 = k0 >>> 0;
|
|
68
|
+
const ks1 = k1 >>> 0;
|
|
69
|
+
const ks2 = (KS_PARITY ^ ks0 ^ ks1) >>> 0;
|
|
70
|
+
const ks = [ks0, ks1, ks2];
|
|
71
|
+
let x0 = c0 + ks0 | 0;
|
|
72
|
+
let x1 = c1 + ks1 | 0;
|
|
73
|
+
for (let round = 0; round < 20; round++) {
|
|
74
|
+
x0 = x0 + x1 | 0;
|
|
75
|
+
const r = ROTATIONS[round & 7];
|
|
76
|
+
x1 = x1 << r | x1 >>> 32 - r;
|
|
77
|
+
x1 ^= x0;
|
|
78
|
+
if ((round & 3) === 3) {
|
|
79
|
+
const s = round + 1 >>> 2;
|
|
80
|
+
x0 = x0 + ks[s % 3] | 0;
|
|
81
|
+
x1 = x1 + ks[(s + 1) % 3] + s | 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
out[0] = x0;
|
|
85
|
+
out[1] = x1;
|
|
86
|
+
}
|
|
87
|
+
const TWO_32 = 4294967296;
|
|
88
|
+
const TWO_53 = 9007199254740992;
|
|
89
|
+
function domainId(name) {
|
|
90
|
+
let h = 2166136261;
|
|
91
|
+
for (let i = 0; i < name.length; i++) {
|
|
92
|
+
h ^= name.charCodeAt(i);
|
|
93
|
+
h = Math.imul(h, 16777619);
|
|
94
|
+
}
|
|
95
|
+
return h >>> 0;
|
|
96
|
+
}
|
|
97
|
+
const DEFAULT_SEED = 0;
|
|
98
|
+
function resolveSeed(seed) {
|
|
99
|
+
const resolved = seed ?? DEFAULT_SEED;
|
|
100
|
+
checkSeed(resolved);
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
103
|
+
function checkSeed(seed) {
|
|
104
|
+
if (!Number.isSafeInteger(seed) || seed < 0) {
|
|
105
|
+
throw new RangeError(`seed must be an integer in [0, 2^53), got ${String(seed)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
class RandomStream {
|
|
109
|
+
/**
|
|
110
|
+
* The stream of one block.
|
|
111
|
+
* @param seed - the caller's seed, an integer in [0, 2^53)
|
|
112
|
+
* @param domain - the generator's fixed domain name
|
|
113
|
+
* @param block - the first block, an integer in [0, 2^32)
|
|
114
|
+
*/
|
|
115
|
+
constructor(seed, domain, block) {
|
|
116
|
+
this.key = new Uint32Array(2);
|
|
117
|
+
this.out = new Uint32Array(2);
|
|
118
|
+
this.counter = 0;
|
|
119
|
+
this.spare = -1;
|
|
120
|
+
checkSeed(seed);
|
|
121
|
+
this.seedLo = seed >>> 0;
|
|
122
|
+
this.seedHi = Math.floor(seed / TWO_32);
|
|
123
|
+
this.domain = domainId(domain);
|
|
124
|
+
this.reset(block);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Restart the stream at the first word of another block of the same seed and domain.
|
|
128
|
+
* @param block - the block, an integer in [0, 2^32)
|
|
129
|
+
*/
|
|
130
|
+
reset(block) {
|
|
131
|
+
threefry2x32(this.seedLo, this.seedHi, this.domain, block >>> 0, this.key);
|
|
132
|
+
this.counter = 0;
|
|
133
|
+
this.spare = -1;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The next word.
|
|
137
|
+
* @returns an unsigned 32-bit integer
|
|
138
|
+
*/
|
|
139
|
+
nextU32() {
|
|
140
|
+
if (this.spare >= 0) {
|
|
141
|
+
const word = this.spare;
|
|
142
|
+
this.spare = -1;
|
|
143
|
+
return word;
|
|
144
|
+
}
|
|
145
|
+
const c = this.counter++;
|
|
146
|
+
threefry2x32(this.key[0], this.key[1], c >>> 0, Math.floor(c / TWO_32), this.out);
|
|
147
|
+
this.spare = this.out[1];
|
|
148
|
+
return this.out[0];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A uniform double in [0, 1) with 53 random bits: (a >>> 5) * 2^26 + (b >>> 6), over 2^53,
|
|
152
|
+
* for the next two words a and b. Exact: every step is an integer below 2^53.
|
|
153
|
+
* @returns the double
|
|
154
|
+
*/
|
|
155
|
+
nextFloat() {
|
|
156
|
+
const a = this.nextU32() >>> 5;
|
|
157
|
+
const b = this.nextU32() >>> 6;
|
|
158
|
+
return (a * 67108864 + b) / TWO_53;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A uniform integer in [0, n) by masked rejection: draw a word, keep its low
|
|
162
|
+
* ceil(log2 n) bits, retry while the result is >= n. Exact and unbiased; fewer than two
|
|
163
|
+
* words per call on average. For n above 2^32 the high bits come from one extra word drawn
|
|
164
|
+
* first.
|
|
165
|
+
* @param n - the bound, an integer in [1, 2^53]
|
|
166
|
+
* @returns the integer
|
|
167
|
+
*/
|
|
168
|
+
nextBelow(n) {
|
|
169
|
+
if (n <= TWO_32) {
|
|
170
|
+
const mask = n === 1 ? 0 : 4294967295 >>> Math.clz32(n - 1);
|
|
171
|
+
for (; ; ) {
|
|
172
|
+
const x = (this.nextU32() & mask) >>> 0;
|
|
173
|
+
if (x < n) {
|
|
174
|
+
return x;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const hiBound = Math.ceil(n / TWO_32);
|
|
179
|
+
const hiMask = 4294967295 >>> Math.clz32(hiBound - 1);
|
|
180
|
+
for (; ; ) {
|
|
181
|
+
const hi = (this.nextU32() & hiMask) >>> 0;
|
|
182
|
+
const x = hi * TWO_32 + this.nextU32();
|
|
183
|
+
if (x < n) {
|
|
184
|
+
return x;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The number of failures before the next success of a Bernoulli(p) sequence, given
|
|
190
|
+
* `logQ = detLog(1 - p)` for 0 < p < 1: floor(detLog(1 - u) / logQ) for u = nextFloat().
|
|
191
|
+
* The caller handles p = 0 and p = 1 without drawing.
|
|
192
|
+
* @param logQ - detLog(1 - p), negative and finite
|
|
193
|
+
* @returns a non-negative integer (possibly above 2^53 for tiny p)
|
|
194
|
+
*/
|
|
195
|
+
nextSkip(logQ) {
|
|
196
|
+
return Math.floor(detLog(1 - this.nextFloat()) / logQ);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function checkInt(name, value, min, max = MAX_COUNT) {
|
|
200
|
+
if (!Number.isInteger(value) || value < min || value > max) {
|
|
201
|
+
throw new RangeError(`${name} must be an integer in [${min}, ${max}], got ${String(value)}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function checkProbability(name, value) {
|
|
205
|
+
if (!(value >= 0 && value <= 1)) {
|
|
206
|
+
throw new RangeError(`${name} must be a probability in [0, 1], got ${String(value)}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function checkEdgeCount(count) {
|
|
210
|
+
if (count > MAX_COUNT) {
|
|
211
|
+
throw new RangeError(`the graph would have ${count} edges, above the limit of ${MAX_COUNT}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
class EdgeBuffer {
|
|
215
|
+
/**
|
|
216
|
+
* An empty buffer.
|
|
217
|
+
* @param capacity - the initial capacity in edges
|
|
218
|
+
*/
|
|
219
|
+
constructor(capacity) {
|
|
220
|
+
this.length = 0;
|
|
221
|
+
const size = Math.max(16, Math.min(capacity, MAX_COUNT));
|
|
222
|
+
this.src = new Uint32Array(size);
|
|
223
|
+
this.dst = new Uint32Array(size);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Append one edge.
|
|
227
|
+
* @param u - the source index
|
|
228
|
+
* @param v - the target index
|
|
229
|
+
*/
|
|
230
|
+
push(u, v) {
|
|
231
|
+
if (this.length === this.src.length) {
|
|
232
|
+
this.grow();
|
|
233
|
+
}
|
|
234
|
+
this.src[this.length] = u;
|
|
235
|
+
this.dst[this.length] = v;
|
|
236
|
+
this.length++;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The edges pushed so far as exactly sized arrays (copies when the buffer has slack).
|
|
240
|
+
* @returns the endpoint arrays
|
|
241
|
+
*/
|
|
242
|
+
finish() {
|
|
243
|
+
if (this.length === this.src.length) {
|
|
244
|
+
return { src: this.src, dst: this.dst };
|
|
245
|
+
}
|
|
246
|
+
return { src: this.src.slice(0, this.length), dst: this.dst.slice(0, this.length) };
|
|
247
|
+
}
|
|
248
|
+
grow() {
|
|
249
|
+
checkEdgeCount(this.length + 1);
|
|
250
|
+
const size = Math.min(this.src.length * 2, MAX_COUNT);
|
|
251
|
+
const src = new Uint32Array(size);
|
|
252
|
+
const dst = new Uint32Array(size);
|
|
253
|
+
src.set(this.src);
|
|
254
|
+
dst.set(this.dst);
|
|
255
|
+
this.src = src;
|
|
256
|
+
this.dst = dst;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function binomialBuffer(pairs, p) {
|
|
260
|
+
const mean = pairs * p;
|
|
261
|
+
checkEdgeCount(mean);
|
|
262
|
+
return new EdgeBuffer(Math.ceil(mean + 6 * Math.sqrt(mean * (1 - p)) + 16));
|
|
263
|
+
}
|
|
264
|
+
function bernoulliSegment(stream, p, logQ, row, lo, hi, out) {
|
|
265
|
+
if (p === 0 || lo >= hi) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (p === 1) {
|
|
269
|
+
for (let c2 = lo; c2 < hi; c2++) {
|
|
270
|
+
out.push(row, c2);
|
|
271
|
+
}
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
let c = lo + stream.nextSkip(logQ);
|
|
275
|
+
while (c < hi) {
|
|
276
|
+
out.push(row, c);
|
|
277
|
+
c += 1 + stream.nextSkip(logQ);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function toGraph(nodeCount, edges, directed, nodeColumns) {
|
|
281
|
+
const { src, dst } = edges.finish();
|
|
282
|
+
return nodeColumns === void 0 ? { directed, nodeCount, src, dst } : { directed, nodeCount, src, dst, nodeColumns };
|
|
283
|
+
}
|
|
284
|
+
function numericColumn(graph, name) {
|
|
285
|
+
const column = graph.nodeColumns?.[name];
|
|
286
|
+
if (column === void 0 || !ArrayBuffer.isView(column)) {
|
|
287
|
+
throw new RangeError(`weights: the graph has no numeric node column "${name}"`);
|
|
288
|
+
}
|
|
289
|
+
return column;
|
|
290
|
+
}
|
|
291
|
+
function drawWeights(graph, spec, seed) {
|
|
292
|
+
const m = graph.src.length;
|
|
293
|
+
const out = new Float32Array(m);
|
|
294
|
+
const { src, dst } = graph;
|
|
295
|
+
const stream = new RandomStream(seed, "weights", 0);
|
|
296
|
+
switch (spec.kind) {
|
|
297
|
+
case "uniform": {
|
|
298
|
+
const min = spec.min ?? 0;
|
|
299
|
+
const max = spec.max ?? 1;
|
|
300
|
+
if (!(Number.isFinite(min) && Number.isFinite(max) && min <= max)) {
|
|
301
|
+
throw new RangeError(`weights: uniform needs finite min <= max, got ${min} and ${max}`);
|
|
302
|
+
}
|
|
303
|
+
for (let e = 0; e < m; e++) {
|
|
304
|
+
out[e] = min + (max - min) * stream.nextFloat();
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
case "integer": {
|
|
309
|
+
const { min, max } = spec;
|
|
310
|
+
if (!(Number.isSafeInteger(min) && Number.isSafeInteger(max) && min <= max)) {
|
|
311
|
+
throw new RangeError(`weights: integer needs integers min <= max, got ${min} and ${max}`);
|
|
312
|
+
}
|
|
313
|
+
for (let e = 0; e < m; e++) {
|
|
314
|
+
out[e] = min + stream.nextBelow(max - min + 1);
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
case "exponential": {
|
|
319
|
+
const mean = spec.mean ?? 1;
|
|
320
|
+
if (!(mean > 0 && Number.isFinite(mean))) {
|
|
321
|
+
throw new RangeError(`weights: exponential needs a positive mean, got ${mean}`);
|
|
322
|
+
}
|
|
323
|
+
for (let e = 0; e < m; e++) {
|
|
324
|
+
out[e] = -mean * detLog(1 - stream.nextFloat());
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case "euclidean": {
|
|
329
|
+
const x = numericColumn(graph, "x");
|
|
330
|
+
const y = numericColumn(graph, "y");
|
|
331
|
+
const z = graph.nodeColumns?.z === void 0 ? null : numericColumn(graph, "z");
|
|
332
|
+
for (let e = 0; e < m; e++) {
|
|
333
|
+
const dx = x[src[e]] - x[dst[e]];
|
|
334
|
+
const dy = y[src[e]] - y[dst[e]];
|
|
335
|
+
const dz = z === null ? 0 : z[src[e]] - z[dst[e]];
|
|
336
|
+
out[e] = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
default: {
|
|
341
|
+
const values = numericColumn(graph, spec.column);
|
|
342
|
+
const combine = spec.combine ?? "sum";
|
|
343
|
+
for (let e = 0; e < m; e++) {
|
|
344
|
+
const a = values[src[e]];
|
|
345
|
+
const b = values[dst[e]];
|
|
346
|
+
out[e] = combineValues(combine, a, b);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
function combineValues(combine, a, b) {
|
|
353
|
+
switch (combine) {
|
|
354
|
+
case "source":
|
|
355
|
+
return a;
|
|
356
|
+
case "target":
|
|
357
|
+
return b;
|
|
358
|
+
case "sum":
|
|
359
|
+
return a + b;
|
|
360
|
+
case "mean":
|
|
361
|
+
return (a + b) / 2;
|
|
362
|
+
case "product":
|
|
363
|
+
return a * b;
|
|
364
|
+
case "min":
|
|
365
|
+
return Math.min(a, b);
|
|
366
|
+
case "max":
|
|
367
|
+
return Math.max(a, b);
|
|
368
|
+
default:
|
|
369
|
+
return Math.abs(a - b);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function withWeights(graph, spec, seed) {
|
|
373
|
+
return { ...graph, weights: drawWeights(graph, spec, resolveSeed(seed)) };
|
|
374
|
+
}
|
|
375
|
+
function applyWeights(graph, options) {
|
|
376
|
+
return options.weights === void 0 ? graph : withWeights(graph, options.weights, options.seed);
|
|
377
|
+
}
|
|
378
|
+
function plantedMatching(n, seed) {
|
|
379
|
+
const stream = new RandomStream(seed, "bipartite-matching", 0);
|
|
380
|
+
const match = new Uint32Array(n);
|
|
381
|
+
for (let i = 0; i < n; i++) {
|
|
382
|
+
match[i] = i;
|
|
383
|
+
}
|
|
384
|
+
for (let i = n - 1; i > 0; i--) {
|
|
385
|
+
const j = stream.nextBelow(i + 1);
|
|
386
|
+
const t = match[i];
|
|
387
|
+
match[i] = match[j];
|
|
388
|
+
match[j] = t;
|
|
389
|
+
}
|
|
390
|
+
return match;
|
|
391
|
+
}
|
|
392
|
+
function bipartiteRows(options, match, start, end, out) {
|
|
393
|
+
const { n1, n2, p } = options;
|
|
394
|
+
const seed = resolveSeed(options.seed);
|
|
395
|
+
const logQ = detLog(1 - p);
|
|
396
|
+
const stream = new RandomStream(seed, "bipartite", 0);
|
|
397
|
+
for (let u = start; u < end; u++) {
|
|
398
|
+
stream.reset(u);
|
|
399
|
+
let pending = match === null ? -1 : match[u];
|
|
400
|
+
const emit = (j) => {
|
|
401
|
+
if (pending >= 0 && pending < j) {
|
|
402
|
+
out.push(u, n1 + pending);
|
|
403
|
+
}
|
|
404
|
+
if (pending >= 0 && pending <= j) {
|
|
405
|
+
pending = -1;
|
|
406
|
+
}
|
|
407
|
+
out.push(u, n1 + j);
|
|
408
|
+
};
|
|
409
|
+
if (p === 1) {
|
|
410
|
+
for (let j = 0; j < n2; j++) {
|
|
411
|
+
emit(j);
|
|
412
|
+
}
|
|
413
|
+
} else if (p > 0) {
|
|
414
|
+
let j = stream.nextSkip(logQ);
|
|
415
|
+
while (j < n2) {
|
|
416
|
+
emit(j);
|
|
417
|
+
j += 1 + stream.nextSkip(logQ);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (pending >= 0) {
|
|
421
|
+
out.push(u, n1 + pending);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function randomBipartiteGraph(options) {
|
|
426
|
+
const { n1, n2, p } = options;
|
|
427
|
+
checkInt("n1", n1, 0);
|
|
428
|
+
checkInt("n2", n2, 0);
|
|
429
|
+
checkInt("n1 + n2", n1 + n2, 0);
|
|
430
|
+
checkProbability("p", p);
|
|
431
|
+
const seed = resolveSeed(options.seed);
|
|
432
|
+
let match = null;
|
|
433
|
+
if (options.perfectMatching === true) {
|
|
434
|
+
if (n1 !== n2) {
|
|
435
|
+
throw new RangeError(`perfectMatching needs n1 === n2, got ${n1} and ${n2}`);
|
|
436
|
+
}
|
|
437
|
+
match = plantedMatching(n1, seed);
|
|
438
|
+
}
|
|
439
|
+
const out = binomialBuffer(n1 * n2, p);
|
|
440
|
+
bipartiteRows(options, match, 0, n1, out);
|
|
441
|
+
const side = new Uint8Array(n1 + n2).fill(1, n1);
|
|
442
|
+
return applyWeights(toGraph(n1 + n2, out, false, { side }), options);
|
|
443
|
+
}
|
|
444
|
+
function planBlocks(options) {
|
|
445
|
+
const { sizes, probabilities } = options;
|
|
446
|
+
const seed = resolveSeed(options.seed);
|
|
447
|
+
const count = sizes.length;
|
|
448
|
+
checkInt("sizes.length", count, 1);
|
|
449
|
+
if (probabilities.length !== count) {
|
|
450
|
+
throw new RangeError(`probabilities must be ${count} x ${count}`);
|
|
451
|
+
}
|
|
452
|
+
const starts = [0];
|
|
453
|
+
for (let b = 0; b < count; b++) {
|
|
454
|
+
checkInt(`sizes[${b}]`, sizes[b], 0);
|
|
455
|
+
starts.push(starts[b] + sizes[b]);
|
|
456
|
+
if (probabilities[b].length !== count) {
|
|
457
|
+
throw new RangeError(`probabilities must be ${count} x ${count}`);
|
|
458
|
+
}
|
|
459
|
+
for (let c = 0; c < count; c++) {
|
|
460
|
+
checkProbability(`probabilities[${b}][${c}]`, probabilities[b][c]);
|
|
461
|
+
if (probabilities[b][c] !== probabilities[c][b]) {
|
|
462
|
+
throw new RangeError(`probabilities must be symmetric: [${b}][${c}] differs from [${c}][${b}]`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const n = starts[count];
|
|
467
|
+
checkInt("the total size", n, 0);
|
|
468
|
+
const block = new Uint32Array(n);
|
|
469
|
+
for (let b = 0; b < count; b++) {
|
|
470
|
+
block.fill(b, starts[b], starts[b + 1]);
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
n,
|
|
474
|
+
seed,
|
|
475
|
+
starts,
|
|
476
|
+
block,
|
|
477
|
+
p: probabilities,
|
|
478
|
+
logQ: probabilities.map((row) => row.map((p) => detLog(1 - p)))
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function blockModelRows(plan, start, end, out) {
|
|
482
|
+
const { starts, block, p, logQ } = plan;
|
|
483
|
+
const stream = new RandomStream(plan.seed, "sbm", 0);
|
|
484
|
+
const count = starts.length - 1;
|
|
485
|
+
for (let u = start; u < end; u++) {
|
|
486
|
+
stream.reset(u);
|
|
487
|
+
const b = block[u];
|
|
488
|
+
for (let c = b; c < count; c++) {
|
|
489
|
+
bernoulliSegment(stream, p[b][c], logQ[b][c], u, c === b ? u + 1 : starts[c], starts[c + 1], out);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function stochasticBlockModelGraph(options) {
|
|
494
|
+
const plan = planBlocks(options);
|
|
495
|
+
let expected = 0;
|
|
496
|
+
let pairs = 0;
|
|
497
|
+
for (let b = 0; b < plan.starts.length - 1; b++) {
|
|
498
|
+
for (let c = b; c < plan.starts.length - 1; c++) {
|
|
499
|
+
const sb = plan.starts[b + 1] - plan.starts[b];
|
|
500
|
+
const sc = plan.starts[c + 1] - plan.starts[c];
|
|
501
|
+
const blockPairs = b === c ? sb * (sb - 1) / 2 : sb * sc;
|
|
502
|
+
expected += blockPairs * plan.p[b][c];
|
|
503
|
+
pairs += blockPairs;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const out = binomialBuffer(pairs, pairs === 0 ? 0 : expected / pairs);
|
|
507
|
+
blockModelRows(plan, 0, plan.n, out);
|
|
508
|
+
return applyWeights(toGraph(plan.n, out, false, { community: plan.block }), options);
|
|
509
|
+
}
|
|
510
|
+
function plantedPartitionGraph(options) {
|
|
511
|
+
const { groups, groupSize, pIn, pOut, seed } = options;
|
|
512
|
+
checkInt("groups", groups, 1);
|
|
513
|
+
checkInt("groupSize", groupSize, 1);
|
|
514
|
+
return stochasticBlockModelGraph({
|
|
515
|
+
sizes: new Array(groups).fill(groupSize),
|
|
516
|
+
probabilities: Array.from(
|
|
517
|
+
{ length: groups },
|
|
518
|
+
(_, b) => Array.from({ length: groups }, (_2, c) => b === c ? pIn : pOut)
|
|
519
|
+
),
|
|
520
|
+
seed,
|
|
521
|
+
weights: options.weights
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
function pushClique(out, lo, size) {
|
|
525
|
+
for (let i = lo; i < lo + size; i++) {
|
|
526
|
+
for (let j = i + 1; j < lo + size; j++) {
|
|
527
|
+
out.push(i, j);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function pathGraph(options) {
|
|
532
|
+
const { n } = options;
|
|
533
|
+
checkInt("n", n, 0);
|
|
534
|
+
const out = new EdgeBuffer(n);
|
|
535
|
+
for (let i = 0; i + 1 < n; i++) {
|
|
536
|
+
out.push(i, i + 1);
|
|
537
|
+
}
|
|
538
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
539
|
+
}
|
|
540
|
+
function cycleGraph(options) {
|
|
541
|
+
const { n } = options;
|
|
542
|
+
checkInt("n", n, 3);
|
|
543
|
+
const out = new EdgeBuffer(n);
|
|
544
|
+
for (let i = 0; i + 1 < n; i++) {
|
|
545
|
+
out.push(i, i + 1);
|
|
546
|
+
}
|
|
547
|
+
out.push(n - 1, 0);
|
|
548
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
549
|
+
}
|
|
550
|
+
function starGraph(options) {
|
|
551
|
+
const { n } = options;
|
|
552
|
+
checkInt("n", n, 1);
|
|
553
|
+
const out = new EdgeBuffer(n);
|
|
554
|
+
for (let i = 1; i < n; i++) {
|
|
555
|
+
out.push(0, i);
|
|
556
|
+
}
|
|
557
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
558
|
+
}
|
|
559
|
+
function wheelGraph(options) {
|
|
560
|
+
const { n } = options;
|
|
561
|
+
checkInt("n", n, 4);
|
|
562
|
+
const out = new EdgeBuffer(2 * n);
|
|
563
|
+
for (let i = 1; i < n; i++) {
|
|
564
|
+
out.push(0, i);
|
|
565
|
+
}
|
|
566
|
+
for (let i = 1; i + 1 < n; i++) {
|
|
567
|
+
out.push(i, i + 1);
|
|
568
|
+
}
|
|
569
|
+
out.push(n - 1, 1);
|
|
570
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
571
|
+
}
|
|
572
|
+
function completeGraph(options) {
|
|
573
|
+
const { n } = options;
|
|
574
|
+
checkInt("n", n, 0);
|
|
575
|
+
const m = n * (n - 1) / 2;
|
|
576
|
+
checkEdgeCount(m);
|
|
577
|
+
const out = new EdgeBuffer(m);
|
|
578
|
+
pushClique(out, 0, n);
|
|
579
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
580
|
+
}
|
|
581
|
+
function completeBipartiteGraph(options) {
|
|
582
|
+
const { a, b } = options;
|
|
583
|
+
checkInt("a", a, 0);
|
|
584
|
+
checkInt("b", b, 0);
|
|
585
|
+
checkInt("a + b", a + b, 0);
|
|
586
|
+
checkEdgeCount(a * b);
|
|
587
|
+
const out = new EdgeBuffer(a * b);
|
|
588
|
+
for (let i = 0; i < a; i++) {
|
|
589
|
+
for (let j = 0; j < b; j++) {
|
|
590
|
+
out.push(i, a + j);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const side = new Uint8Array(a + b).fill(1, a);
|
|
594
|
+
return applyWeights(toGraph(a + b, out, false, { side }), options);
|
|
595
|
+
}
|
|
596
|
+
function hypercubeGraph(options) {
|
|
597
|
+
const { dimension } = options;
|
|
598
|
+
checkInt("dimension", dimension, 0, 26);
|
|
599
|
+
const n = 2 ** dimension;
|
|
600
|
+
const out = new EdgeBuffer(dimension * n / 2);
|
|
601
|
+
for (let i = 0; i < n; i++) {
|
|
602
|
+
for (let b = 0; b < dimension; b++) {
|
|
603
|
+
const j = i | 1 << b;
|
|
604
|
+
if (j !== i) {
|
|
605
|
+
out.push(i, j);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
610
|
+
}
|
|
611
|
+
function ladderGraph(options) {
|
|
612
|
+
const { n } = options;
|
|
613
|
+
checkInt("n", n, 1);
|
|
614
|
+
checkInt("2n", 2 * n, 2);
|
|
615
|
+
return applyWeights(toGraph(2 * n, ladderEdges(n), false), options);
|
|
616
|
+
}
|
|
617
|
+
function barbellGraph(options) {
|
|
618
|
+
const { cliqueSize: m1, pathLength: m2 } = options;
|
|
619
|
+
checkInt("cliqueSize", m1, 2);
|
|
620
|
+
checkInt("pathLength", m2, 0);
|
|
621
|
+
const n = 2 * m1 + m2;
|
|
622
|
+
checkInt("2 * cliqueSize + pathLength", n, 4);
|
|
623
|
+
checkEdgeCount(m1 * (m1 - 1) + m2 + 1);
|
|
624
|
+
const out = new EdgeBuffer(m1 * (m1 - 1) + m2 + 1);
|
|
625
|
+
pushClique(out, 0, m1);
|
|
626
|
+
for (let i = m1 - 1; i < m1 + m2; i++) {
|
|
627
|
+
out.push(i, i + 1);
|
|
628
|
+
}
|
|
629
|
+
pushClique(out, m1 + m2, m1);
|
|
630
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
631
|
+
}
|
|
632
|
+
function lollipopGraph(options) {
|
|
633
|
+
const { cliqueSize: m, pathLength } = options;
|
|
634
|
+
checkInt("cliqueSize", m, 2);
|
|
635
|
+
checkInt("pathLength", pathLength, 0);
|
|
636
|
+
checkInt("cliqueSize + pathLength", m + pathLength, 2);
|
|
637
|
+
checkEdgeCount(m * (m - 1) / 2 + pathLength);
|
|
638
|
+
const out = new EdgeBuffer(m * (m - 1) / 2 + pathLength);
|
|
639
|
+
pushClique(out, 0, m);
|
|
640
|
+
for (let i = m - 1; i + 1 < m + pathLength; i++) {
|
|
641
|
+
out.push(i, i + 1);
|
|
642
|
+
}
|
|
643
|
+
return applyWeights(toGraph(m + pathLength, out, false), options);
|
|
644
|
+
}
|
|
645
|
+
function groupLabels(cliques, size) {
|
|
646
|
+
const community = new Uint32Array(cliques * size);
|
|
647
|
+
for (let g = 0; g < cliques; g++) {
|
|
648
|
+
community.fill(g, g * size, (g + 1) * size);
|
|
649
|
+
}
|
|
650
|
+
return community;
|
|
651
|
+
}
|
|
652
|
+
function cavemanGraph(options) {
|
|
653
|
+
const { cliques, size } = options;
|
|
654
|
+
checkInt("cliques", cliques, 1);
|
|
655
|
+
checkInt("size", size, 1);
|
|
656
|
+
const n = cliques * size;
|
|
657
|
+
checkInt("cliques * size", n, 1);
|
|
658
|
+
checkEdgeCount(n * (size - 1) / 2);
|
|
659
|
+
const out = new EdgeBuffer(n * (size - 1) / 2);
|
|
660
|
+
for (let g = 0; g < cliques; g++) {
|
|
661
|
+
pushClique(out, g * size, size);
|
|
662
|
+
}
|
|
663
|
+
return applyWeights(toGraph(n, out, false, { community: groupLabels(cliques, size) }), options);
|
|
664
|
+
}
|
|
665
|
+
function connectedCavemanGraph(options) {
|
|
666
|
+
const { cliques, size } = options;
|
|
667
|
+
checkInt("cliques", cliques, 2);
|
|
668
|
+
checkInt("size", size, 2);
|
|
669
|
+
const n = cliques * size;
|
|
670
|
+
checkInt("cliques * size", n, 4);
|
|
671
|
+
checkEdgeCount(n * (size - 1) / 2);
|
|
672
|
+
const out = new EdgeBuffer(n * (size - 1) / 2);
|
|
673
|
+
for (let g = 0; g < cliques; g++) {
|
|
674
|
+
const s = g * size;
|
|
675
|
+
for (let i = s; i < s + size; i++) {
|
|
676
|
+
for (let j = i + 1; j < s + size; j++) {
|
|
677
|
+
if (i === s && j === s + 1) {
|
|
678
|
+
out.push(s, (s - 1 + n) % n);
|
|
679
|
+
} else {
|
|
680
|
+
out.push(i, j);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return applyWeights(toGraph(n, out, false, { community: groupLabels(cliques, size) }), options);
|
|
686
|
+
}
|
|
687
|
+
function balancedTreeGraph(options) {
|
|
688
|
+
const { branching: r, height: h } = options;
|
|
689
|
+
checkInt("branching", r, 1);
|
|
690
|
+
checkInt("height", h, 0);
|
|
691
|
+
let n = 0;
|
|
692
|
+
let level = 1;
|
|
693
|
+
for (let d = 0; d <= h; d++) {
|
|
694
|
+
n += level;
|
|
695
|
+
level *= r;
|
|
696
|
+
checkInt("node count", n, 1);
|
|
697
|
+
}
|
|
698
|
+
const out = new EdgeBuffer(n);
|
|
699
|
+
for (let c = 1; c < n; c++) {
|
|
700
|
+
out.push(Math.floor((c - 1) / r), c);
|
|
701
|
+
}
|
|
702
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
703
|
+
}
|
|
704
|
+
function petersenGraph(options = {}) {
|
|
705
|
+
const out = new EdgeBuffer(15);
|
|
706
|
+
for (let i = 0; i < 5; i++) {
|
|
707
|
+
out.push(i, (i + 1) % 5);
|
|
708
|
+
}
|
|
709
|
+
for (let i = 0; i < 5; i++) {
|
|
710
|
+
out.push(i, i + 5);
|
|
711
|
+
}
|
|
712
|
+
for (let i = 0; i < 5; i++) {
|
|
713
|
+
out.push(5 + i, 5 + (i + 2) % 5);
|
|
714
|
+
}
|
|
715
|
+
return applyWeights(toGraph(10, out, false), options);
|
|
716
|
+
}
|
|
717
|
+
function emptyGraph(options) {
|
|
718
|
+
checkInt("n", options.n, 0);
|
|
719
|
+
return applyWeights(toGraph(options.n, new EdgeBuffer(0), false), options);
|
|
720
|
+
}
|
|
721
|
+
function completeMultipartiteGraph(options) {
|
|
722
|
+
const { sizes } = options;
|
|
723
|
+
const starts = [0];
|
|
724
|
+
for (let p = 0; p < sizes.length; p++) {
|
|
725
|
+
checkInt(`sizes[${p}]`, sizes[p], 0);
|
|
726
|
+
starts.push(starts[p] + sizes[p]);
|
|
727
|
+
}
|
|
728
|
+
const n = starts[sizes.length];
|
|
729
|
+
checkInt("the total size", n, 0);
|
|
730
|
+
let m = n * (n - 1) / 2;
|
|
731
|
+
for (const size of sizes) {
|
|
732
|
+
m -= size * (size - 1) / 2;
|
|
733
|
+
}
|
|
734
|
+
checkEdgeCount(m);
|
|
735
|
+
const part = new Uint32Array(n);
|
|
736
|
+
for (let p = 0; p < sizes.length; p++) {
|
|
737
|
+
part.fill(p, starts[p], starts[p + 1]);
|
|
738
|
+
}
|
|
739
|
+
const out = new EdgeBuffer(m);
|
|
740
|
+
for (let u = 0; u < n; u++) {
|
|
741
|
+
for (let v = starts[part[u] + 1]; v < n; v++) {
|
|
742
|
+
out.push(u, v);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return applyWeights(toGraph(n, out, false, { part }), options);
|
|
746
|
+
}
|
|
747
|
+
function circularLadderGraph(options) {
|
|
748
|
+
const { n } = options;
|
|
749
|
+
checkInt("n", n, 3);
|
|
750
|
+
checkEdgeCount(3 * n);
|
|
751
|
+
const out = ladderEdges(n);
|
|
752
|
+
out.push(n - 1, 0);
|
|
753
|
+
out.push(2 * n - 1, n);
|
|
754
|
+
return applyWeights(toGraph(2 * n, out, false), options);
|
|
755
|
+
}
|
|
756
|
+
function mobiusLadderGraph(options) {
|
|
757
|
+
const { n } = options;
|
|
758
|
+
checkInt("n", n, 3);
|
|
759
|
+
checkEdgeCount(3 * n);
|
|
760
|
+
const out = new EdgeBuffer(3 * n);
|
|
761
|
+
for (let i = 0; i + 1 < 2 * n; i++) {
|
|
762
|
+
out.push(i, i + 1);
|
|
763
|
+
}
|
|
764
|
+
out.push(2 * n - 1, 0);
|
|
765
|
+
for (let i = 0; i < n; i++) {
|
|
766
|
+
out.push(i, i + n);
|
|
767
|
+
}
|
|
768
|
+
return applyWeights(toGraph(2 * n, out, false), options);
|
|
769
|
+
}
|
|
770
|
+
function ladderEdges(n) {
|
|
771
|
+
const out = new EdgeBuffer(3 * n);
|
|
772
|
+
for (let side = 0; side < 2; side++) {
|
|
773
|
+
for (let i = 0; i + 1 < n; i++) {
|
|
774
|
+
out.push(side * n + i, side * n + i + 1);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
for (let i = 0; i < n; i++) {
|
|
778
|
+
out.push(i, n + i);
|
|
779
|
+
}
|
|
780
|
+
return out;
|
|
781
|
+
}
|
|
782
|
+
function ringOfCliquesGraph(options) {
|
|
783
|
+
const { cliques, size } = options;
|
|
784
|
+
checkInt("cliques", cliques, 2);
|
|
785
|
+
checkInt("size", size, 2);
|
|
786
|
+
const n = cliques * size;
|
|
787
|
+
checkInt("cliques * size", n, 4);
|
|
788
|
+
const m = n * (size - 1) / 2 + cliques;
|
|
789
|
+
checkEdgeCount(m);
|
|
790
|
+
const out = new EdgeBuffer(m);
|
|
791
|
+
for (let g = 0; g < cliques; g++) {
|
|
792
|
+
pushClique(out, g * size, size);
|
|
793
|
+
out.push(g * size + 1, (g + 1) * size % n);
|
|
794
|
+
}
|
|
795
|
+
return applyWeights(toGraph(n, out, false, { community: groupLabels(cliques, size) }), options);
|
|
796
|
+
}
|
|
797
|
+
const O_THRESHOLD = 709.782712893384;
|
|
798
|
+
const U_THRESHOLD = -745.1332191019411;
|
|
799
|
+
const LN2_HI = 0.6931471803691238;
|
|
800
|
+
const LN2_LO = 19082149292705877e-26;
|
|
801
|
+
const INV_LN2 = 1.4426950408889634;
|
|
802
|
+
const TWOM1000 = 9332636185032189e-317;
|
|
803
|
+
const P1 = 0.16666666666666602;
|
|
804
|
+
const P2 = -0.0027777777777015593;
|
|
805
|
+
const P3 = 6613756321437934e-20;
|
|
806
|
+
const P4 = -16533902205465252e-22;
|
|
807
|
+
const P5 = 41381367970572385e-24;
|
|
808
|
+
const view = new DataView(new ArrayBuffer(8));
|
|
809
|
+
function scale(y, k) {
|
|
810
|
+
view.setFloat64(0, y, true);
|
|
811
|
+
view.setInt32(4, view.getInt32(4, true) + (k << 20), true);
|
|
812
|
+
return view.getFloat64(0, true);
|
|
813
|
+
}
|
|
814
|
+
function detExp(x) {
|
|
815
|
+
view.setFloat64(0, x, true);
|
|
816
|
+
const high = view.getInt32(4, true);
|
|
817
|
+
const negative = high < 0 ? 1 : 0;
|
|
818
|
+
const hx = high & 2147483647;
|
|
819
|
+
if (hx >= 1082535490) {
|
|
820
|
+
if (hx >= 2146435072) {
|
|
821
|
+
return negative === 0 || Number.isNaN(x) ? x + x : 0;
|
|
822
|
+
}
|
|
823
|
+
if (x > O_THRESHOLD) {
|
|
824
|
+
return Infinity;
|
|
825
|
+
}
|
|
826
|
+
if (x < U_THRESHOLD) {
|
|
827
|
+
return 0;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
let hi = 0;
|
|
831
|
+
let lo = 0;
|
|
832
|
+
let k = 0;
|
|
833
|
+
let r = x;
|
|
834
|
+
if (hx > 1071001154) {
|
|
835
|
+
if (hx < 1072734898) {
|
|
836
|
+
hi = negative === 0 ? x - LN2_HI : x + LN2_HI;
|
|
837
|
+
lo = negative === 0 ? LN2_LO : -LN2_LO;
|
|
838
|
+
k = 1 - negative - negative;
|
|
839
|
+
} else {
|
|
840
|
+
k = Math.trunc(INV_LN2 * x + (negative === 0 ? 0.5 : -0.5));
|
|
841
|
+
hi = x - k * LN2_HI;
|
|
842
|
+
lo = k * LN2_LO;
|
|
843
|
+
}
|
|
844
|
+
r = hi - lo;
|
|
845
|
+
} else if (hx < 1043333120) {
|
|
846
|
+
return 1 + x;
|
|
847
|
+
}
|
|
848
|
+
const t = r * r;
|
|
849
|
+
const c = r - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
|
|
850
|
+
if (k === 0) {
|
|
851
|
+
return 1 - (r * c / (c - 2) - r);
|
|
852
|
+
}
|
|
853
|
+
const y = 1 - (lo - r * c / (2 - c) - hi);
|
|
854
|
+
return k >= -1021 ? scale(y, k) : scale(y, k + 1e3) * TWOM1000;
|
|
855
|
+
}
|
|
856
|
+
function detPow(x, y) {
|
|
857
|
+
if (x === 0) {
|
|
858
|
+
if (y === 0) {
|
|
859
|
+
return 1;
|
|
860
|
+
}
|
|
861
|
+
return y > 0 ? 0 : Infinity;
|
|
862
|
+
}
|
|
863
|
+
return detExp(y * detLog(x));
|
|
864
|
+
}
|
|
865
|
+
const MAX_POWER_LAW_SUPPORT = 2 ** 24;
|
|
866
|
+
function checkExponent(name, exponent) {
|
|
867
|
+
if (!(Number.isFinite(exponent) && exponent >= 0)) {
|
|
868
|
+
throw new RangeError(`${name} must be a finite number >= 0, got ${String(exponent)}`);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function powerLawSampler(names, exponent, min, max) {
|
|
872
|
+
checkExponent(names[0], exponent);
|
|
873
|
+
checkInt(names[1], min, 1);
|
|
874
|
+
checkInt(names[2], max, min, Math.min(MAX_COUNT, min + MAX_POWER_LAW_SUPPORT - 1));
|
|
875
|
+
const count = max - min + 1;
|
|
876
|
+
const cdf = new Float64Array(count);
|
|
877
|
+
let total = 0;
|
|
878
|
+
for (let k = 0; k < count; k++) {
|
|
879
|
+
total += detPow(min + k, -exponent);
|
|
880
|
+
cdf[k] = total;
|
|
881
|
+
}
|
|
882
|
+
return (stream) => {
|
|
883
|
+
const u = stream.nextFloat() * total;
|
|
884
|
+
let lo = 0;
|
|
885
|
+
let hi = count - 1;
|
|
886
|
+
while (lo < hi) {
|
|
887
|
+
const mid = lo + hi >>> 1;
|
|
888
|
+
if (cdf[mid] > u) {
|
|
889
|
+
hi = mid;
|
|
890
|
+
} else {
|
|
891
|
+
lo = mid + 1;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return min + lo;
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
function makeSumEven(seq, min, max) {
|
|
898
|
+
let total = 0;
|
|
899
|
+
for (const d of seq) {
|
|
900
|
+
total += d;
|
|
901
|
+
}
|
|
902
|
+
if (total % 2 === 0) {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const last = seq.length - 1;
|
|
906
|
+
if (seq[last] < max) {
|
|
907
|
+
seq[last]++;
|
|
908
|
+
} else if (seq[last] > min) {
|
|
909
|
+
seq[last]--;
|
|
910
|
+
} else {
|
|
911
|
+
throw new RangeError(`cannot make the degree sum even: every degree is ${min} and n is odd`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
function powerLawDegreeSequence(options) {
|
|
915
|
+
const { n, exponent, minDegree, maxDegree } = options;
|
|
916
|
+
const seed = resolveSeed(options.seed);
|
|
917
|
+
checkInt("n", n, 0);
|
|
918
|
+
const sample = powerLawSampler(["exponent", "minDegree", "maxDegree"], exponent, minDegree, maxDegree);
|
|
919
|
+
const stream = new RandomStream(seed, "power-law", 0);
|
|
920
|
+
const seq = new Uint32Array(n);
|
|
921
|
+
for (let i = 0; i < n; i++) {
|
|
922
|
+
seq[i] = sample(stream);
|
|
923
|
+
}
|
|
924
|
+
if ((options.evenSum ?? true) && n > 0) {
|
|
925
|
+
makeSumEven(seq, minDegree, maxDegree);
|
|
926
|
+
}
|
|
927
|
+
return seq;
|
|
928
|
+
}
|
|
929
|
+
function degreeSum(name, degrees) {
|
|
930
|
+
checkInt(`${name}.length`, degrees.length, 0);
|
|
931
|
+
let total = 0;
|
|
932
|
+
for (let i = 0; i < degrees.length; i++) {
|
|
933
|
+
const d = degrees[i];
|
|
934
|
+
if (!(Number.isInteger(d) && d >= 0 && d <= MAX_COUNT)) {
|
|
935
|
+
checkInt(`${name}[${i}]`, d, 0);
|
|
936
|
+
}
|
|
937
|
+
total += d;
|
|
938
|
+
}
|
|
939
|
+
checkEdgeCount(total / 2);
|
|
940
|
+
return total;
|
|
941
|
+
}
|
|
942
|
+
function keeps(name, value) {
|
|
943
|
+
if (value !== void 0 && value !== "keep" && value !== "erase") {
|
|
944
|
+
throw new RangeError(`${name} must be "keep" or "erase", got ${String(value)}`);
|
|
945
|
+
}
|
|
946
|
+
return value === "keep";
|
|
947
|
+
}
|
|
948
|
+
function stubList(degrees, total, offset = 0) {
|
|
949
|
+
const stubs = new Uint32Array(total);
|
|
950
|
+
let k = 0;
|
|
951
|
+
for (let i = 0; i < degrees.length; i++) {
|
|
952
|
+
stubs.fill(i + offset, k, k + degrees[i]);
|
|
953
|
+
k += degrees[i];
|
|
954
|
+
}
|
|
955
|
+
return stubs;
|
|
956
|
+
}
|
|
957
|
+
function shuffle(a, stream) {
|
|
958
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
959
|
+
const j = stream.nextBelow(i + 1);
|
|
960
|
+
const t = a[i];
|
|
961
|
+
a[i] = a[j];
|
|
962
|
+
a[j] = t;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
function eraseEdges(n, src, dst, directed, keepLoops, keepMulti, out, group) {
|
|
966
|
+
const m = src.length;
|
|
967
|
+
const keep = new Uint8Array(m).fill(1);
|
|
968
|
+
for (let e = 0; e < m; e++) {
|
|
969
|
+
const u = src[e];
|
|
970
|
+
const v = dst[e];
|
|
971
|
+
if (!keepLoops && u === v || group !== void 0 && group[u] === group[v]) {
|
|
972
|
+
keep[e] = 0;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
if (!keepMulti) {
|
|
976
|
+
const lo = (e) => directed ? src[e] : Math.min(src[e], dst[e]);
|
|
977
|
+
const start = new Uint32Array(n + 1);
|
|
978
|
+
for (let e = 0; e < m; e++) {
|
|
979
|
+
start[lo(e) + 1]++;
|
|
980
|
+
}
|
|
981
|
+
for (let i = 0; i < n; i++) {
|
|
982
|
+
start[i + 1] += start[i];
|
|
983
|
+
}
|
|
984
|
+
const fill = start.slice(0, n);
|
|
985
|
+
const order = new Uint32Array(m);
|
|
986
|
+
for (let e = 0; e < m; e++) {
|
|
987
|
+
order[fill[lo(e)]++] = e;
|
|
988
|
+
}
|
|
989
|
+
const mark = new Uint32Array(n);
|
|
990
|
+
for (let k = 0; k < m; k++) {
|
|
991
|
+
const e = order[k];
|
|
992
|
+
if (keep[e] === 0) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
const u = lo(e);
|
|
996
|
+
const v = directed ? dst[e] : Math.max(src[e], dst[e]);
|
|
997
|
+
if (mark[v] === u + 1) {
|
|
998
|
+
keep[e] = 0;
|
|
999
|
+
} else {
|
|
1000
|
+
mark[v] = u + 1;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
for (let e = 0; e < m; e++) {
|
|
1005
|
+
if (keep[e] === 1) {
|
|
1006
|
+
out.push(src[e], dst[e]);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
function pairStubs(n, stubs, keepLoops, keepMulti, out, group) {
|
|
1011
|
+
const m = stubs.length / 2;
|
|
1012
|
+
const src = new Uint32Array(m);
|
|
1013
|
+
const dst = new Uint32Array(m);
|
|
1014
|
+
for (let k = 0; k < m; k++) {
|
|
1015
|
+
src[k] = stubs[2 * k];
|
|
1016
|
+
dst[k] = stubs[2 * k + 1];
|
|
1017
|
+
}
|
|
1018
|
+
eraseEdges(n, src, dst, false, keepLoops, keepMulti, out, group);
|
|
1019
|
+
}
|
|
1020
|
+
function configurationModelGraph(options) {
|
|
1021
|
+
const { degrees } = options;
|
|
1022
|
+
const seed = resolveSeed(options.seed);
|
|
1023
|
+
const keepLoops = keeps("selfLoops", options.selfLoops);
|
|
1024
|
+
const keepMulti = keeps("multiEdges", options.multiEdges);
|
|
1025
|
+
const total = degreeSum("degrees", degrees);
|
|
1026
|
+
if (total % 2 !== 0) {
|
|
1027
|
+
throw new RangeError(`the degree sum must be even, got ${total}`);
|
|
1028
|
+
}
|
|
1029
|
+
const n = degrees.length;
|
|
1030
|
+
const stubs = stubList(degrees, total);
|
|
1031
|
+
shuffle(stubs, new RandomStream(seed, "configuration", 0));
|
|
1032
|
+
const out = new EdgeBuffer(total / 2);
|
|
1033
|
+
pairStubs(n, stubs, keepLoops, keepMulti, out);
|
|
1034
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
1035
|
+
}
|
|
1036
|
+
function directedConfigurationModelGraph(options) {
|
|
1037
|
+
const { outDegrees, inDegrees } = options;
|
|
1038
|
+
const seed = resolveSeed(options.seed);
|
|
1039
|
+
const keepLoops = keeps("selfLoops", options.selfLoops);
|
|
1040
|
+
const keepMulti = keeps("multiEdges", options.multiEdges);
|
|
1041
|
+
const outTotal = degreeSum("outDegrees", outDegrees);
|
|
1042
|
+
const inTotal = degreeSum("inDegrees", inDegrees);
|
|
1043
|
+
if (outDegrees.length !== inDegrees.length) {
|
|
1044
|
+
throw new RangeError(`outDegrees and inDegrees must have the same length`);
|
|
1045
|
+
}
|
|
1046
|
+
if (outTotal !== inTotal) {
|
|
1047
|
+
throw new RangeError(`the out-degree sum (${outTotal}) must equal the in-degree sum (${inTotal})`);
|
|
1048
|
+
}
|
|
1049
|
+
const n = outDegrees.length;
|
|
1050
|
+
const dst = stubList(inDegrees, inTotal);
|
|
1051
|
+
shuffle(dst, new RandomStream(seed, "configuration-directed", 0));
|
|
1052
|
+
const out = new EdgeBuffer(outTotal);
|
|
1053
|
+
eraseEdges(n, stubList(outDegrees, outTotal), dst, true, keepLoops, keepMulti, out);
|
|
1054
|
+
return applyWeights(toGraph(n, out, true), options);
|
|
1055
|
+
}
|
|
1056
|
+
function bipartiteConfigurationModelGraph(options) {
|
|
1057
|
+
const { leftDegrees, rightDegrees } = options;
|
|
1058
|
+
const seed = resolveSeed(options.seed);
|
|
1059
|
+
const keepMulti = keeps("multiEdges", options.multiEdges);
|
|
1060
|
+
const leftTotal = degreeSum("leftDegrees", leftDegrees);
|
|
1061
|
+
const rightTotal = degreeSum("rightDegrees", rightDegrees);
|
|
1062
|
+
if (leftTotal !== rightTotal) {
|
|
1063
|
+
throw new RangeError(`the left degree sum (${leftTotal}) must equal the right degree sum (${rightTotal})`);
|
|
1064
|
+
}
|
|
1065
|
+
const left = leftDegrees.length;
|
|
1066
|
+
const n = left + rightDegrees.length;
|
|
1067
|
+
checkInt("the node count", n, 0);
|
|
1068
|
+
const dst = stubList(rightDegrees, rightTotal, left);
|
|
1069
|
+
shuffle(dst, new RandomStream(seed, "bipartite-configuration", 0));
|
|
1070
|
+
const out = new EdgeBuffer(leftTotal);
|
|
1071
|
+
eraseEdges(n, stubList(leftDegrees, leftTotal), dst, false, true, keepMulti, out);
|
|
1072
|
+
const side = new Uint8Array(n).fill(1, left);
|
|
1073
|
+
return applyWeights(toGraph(n, out, false, { side }), options);
|
|
1074
|
+
}
|
|
1075
|
+
function weightSum(weights) {
|
|
1076
|
+
checkInt("expectedDegrees.length", weights.length, 0);
|
|
1077
|
+
let total = 0;
|
|
1078
|
+
for (let i = 0; i < weights.length; i++) {
|
|
1079
|
+
if (!(Number.isFinite(weights[i]) && weights[i] >= 0)) {
|
|
1080
|
+
throw new RangeError(`expectedDegrees[${i}] must be a finite number >= 0, got ${String(weights[i])}`);
|
|
1081
|
+
}
|
|
1082
|
+
total += weights[i];
|
|
1083
|
+
}
|
|
1084
|
+
return total;
|
|
1085
|
+
}
|
|
1086
|
+
function planRows(weights, nodeStarts, factor, seed, domain, total) {
|
|
1087
|
+
const n = weights.length;
|
|
1088
|
+
const order = new Uint32Array(n);
|
|
1089
|
+
const rowBlock = new Uint32Array(n);
|
|
1090
|
+
for (let i = 0; i < n; i++) {
|
|
1091
|
+
order[i] = i;
|
|
1092
|
+
}
|
|
1093
|
+
for (let b = 0; b + 1 < nodeStarts.length; b++) {
|
|
1094
|
+
order.subarray(nodeStarts[b], nodeStarts[b + 1]).sort((x, y) => weights[y] - weights[x] || x - y);
|
|
1095
|
+
rowBlock.fill(b, nodeStarts[b], nodeStarts[b + 1]);
|
|
1096
|
+
}
|
|
1097
|
+
const weight = Float64Array.from(order, (i) => weights[i]);
|
|
1098
|
+
return { n, seed, domain, order, weight, rowBlock, starts: nodeStarts, factor, total };
|
|
1099
|
+
}
|
|
1100
|
+
function planChungLu(options) {
|
|
1101
|
+
const seed = resolveSeed(options.seed);
|
|
1102
|
+
const total = weightSum(options.expectedDegrees);
|
|
1103
|
+
const n = options.expectedDegrees.length;
|
|
1104
|
+
return planRows(options.expectedDegrees, [0, n], [[total > 0 ? 1 / total : 0]], seed, "chung-lu", total);
|
|
1105
|
+
}
|
|
1106
|
+
function chungLuRows(plan, start, end, out) {
|
|
1107
|
+
const { order, weight, rowBlock, starts, factor } = plan;
|
|
1108
|
+
const stream = new RandomStream(plan.seed, plan.domain, 0);
|
|
1109
|
+
const count = starts.length - 1;
|
|
1110
|
+
for (let row = start; row < end; row++) {
|
|
1111
|
+
stream.reset(row);
|
|
1112
|
+
const r = rowBlock[row];
|
|
1113
|
+
for (let s = r; s < count; s++) {
|
|
1114
|
+
const cu = factor[r][s] * weight[row];
|
|
1115
|
+
let v = s === r ? row + 1 : starts[s];
|
|
1116
|
+
const hi = starts[s + 1];
|
|
1117
|
+
if (cu === 0 || v >= hi) {
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
let p = Math.min(1, cu * weight[v]);
|
|
1121
|
+
while (v < hi && p > 0) {
|
|
1122
|
+
if (p !== 1) {
|
|
1123
|
+
const logQ = detLog(1 - p);
|
|
1124
|
+
if (logQ === 0) {
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
v += stream.nextSkip(logQ);
|
|
1128
|
+
if (v >= hi) {
|
|
1129
|
+
break;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
const q = Math.min(1, cu * weight[v]);
|
|
1133
|
+
if (stream.nextFloat() < q / p) {
|
|
1134
|
+
out.push(order[row], order[v]);
|
|
1135
|
+
}
|
|
1136
|
+
p = q;
|
|
1137
|
+
v++;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function runPlan(plan, nodeColumns, options) {
|
|
1143
|
+
const mean = plan.total / 2;
|
|
1144
|
+
checkEdgeCount(mean);
|
|
1145
|
+
const out = new EdgeBuffer(Math.ceil(mean + 6 * Math.sqrt(mean) + 16));
|
|
1146
|
+
chungLuRows(plan, 0, plan.n, out);
|
|
1147
|
+
return applyWeights(toGraph(plan.n, out, false, nodeColumns), options);
|
|
1148
|
+
}
|
|
1149
|
+
function chungLuGraph(options) {
|
|
1150
|
+
return runPlan(planChungLu(options), void 0, options);
|
|
1151
|
+
}
|
|
1152
|
+
function planDegreeCorrectedSbm(options) {
|
|
1153
|
+
const { sizes, expectedDegrees, mixing } = options;
|
|
1154
|
+
const seed = resolveSeed(options.seed);
|
|
1155
|
+
checkInt("sizes.length", sizes.length, 1);
|
|
1156
|
+
checkProbability("mixing", mixing);
|
|
1157
|
+
const total = weightSum(expectedDegrees);
|
|
1158
|
+
const starts = [0];
|
|
1159
|
+
for (let b = 0; b < sizes.length; b++) {
|
|
1160
|
+
checkInt(`sizes[${b}]`, sizes[b], 0);
|
|
1161
|
+
starts.push(starts[b] + sizes[b]);
|
|
1162
|
+
}
|
|
1163
|
+
if (starts[sizes.length] !== expectedDegrees.length) {
|
|
1164
|
+
throw new RangeError(
|
|
1165
|
+
`the sizes sum to ${starts[sizes.length]}, but expectedDegrees has ${expectedDegrees.length} entries`
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
const kappa = sizes.map((_, b) => {
|
|
1169
|
+
let k = 0;
|
|
1170
|
+
for (let i = starts[b]; i < starts[b + 1]; i++) {
|
|
1171
|
+
k += expectedDegrees[i];
|
|
1172
|
+
}
|
|
1173
|
+
return k;
|
|
1174
|
+
});
|
|
1175
|
+
const between = total > 0 ? mixing / total : 0;
|
|
1176
|
+
const inside = kappa.map((kr) => kr > 0 ? (1 - mixing) / kr : 0);
|
|
1177
|
+
const factor = kappa.map((_, r) => kappa.map((_2, s) => r === s ? inside[r] : between));
|
|
1178
|
+
return planRows(expectedDegrees, starts, factor, seed, "dcsbm", total);
|
|
1179
|
+
}
|
|
1180
|
+
function degreeCorrectedSbmGraph(options) {
|
|
1181
|
+
const plan = planDegreeCorrectedSbm(options);
|
|
1182
|
+
const community = new Uint32Array(plan.n);
|
|
1183
|
+
for (let b = 0; b + 1 < plan.starts.length; b++) {
|
|
1184
|
+
community.fill(b, plan.starts[b], plan.starts[b + 1]);
|
|
1185
|
+
}
|
|
1186
|
+
return runPlan(plan, { community }, options);
|
|
1187
|
+
}
|
|
1188
|
+
const REGULAR_ATTEMPTS = 100;
|
|
1189
|
+
const REGULAR_ROUNDS = 1e3;
|
|
1190
|
+
function regularAttempt(n, d, stream, adj, deg) {
|
|
1191
|
+
deg.fill(0);
|
|
1192
|
+
const has = (u, v) => {
|
|
1193
|
+
const base = u * d;
|
|
1194
|
+
for (let k = base; k < base + deg[u]; k++) {
|
|
1195
|
+
if (adj[k] === v) {
|
|
1196
|
+
return true;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
return false;
|
|
1200
|
+
};
|
|
1201
|
+
const potential = new Uint32Array(n);
|
|
1202
|
+
let stubs = stubList(new Uint32Array(n).fill(d), n * d);
|
|
1203
|
+
for (let round = 0; round < REGULAR_ROUNDS && stubs.length > 0; round++) {
|
|
1204
|
+
shuffle(stubs, stream);
|
|
1205
|
+
const touched = [];
|
|
1206
|
+
for (let k2 = 0; k2 < stubs.length; k2 += 2) {
|
|
1207
|
+
const u = Math.min(stubs[k2], stubs[k2 + 1]);
|
|
1208
|
+
const v = Math.max(stubs[k2], stubs[k2 + 1]);
|
|
1209
|
+
if (u !== v && !has(u, v)) {
|
|
1210
|
+
adj[u * d + deg[u]++] = v;
|
|
1211
|
+
adj[v * d + deg[v]++] = u;
|
|
1212
|
+
} else {
|
|
1213
|
+
for (const x of [u, v]) {
|
|
1214
|
+
if (potential[x]++ === 0) {
|
|
1215
|
+
touched.push(x);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
touched.sort((a, b) => a - b);
|
|
1221
|
+
let suitable = touched.length === 0;
|
|
1222
|
+
for (let i = 0; i < touched.length && !suitable; i++) {
|
|
1223
|
+
for (let j = i + 1; j < touched.length && !suitable; j++) {
|
|
1224
|
+
suitable = !has(touched[i], touched[j]);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (!suitable) {
|
|
1228
|
+
return false;
|
|
1229
|
+
}
|
|
1230
|
+
let total = 0;
|
|
1231
|
+
for (const x of touched) {
|
|
1232
|
+
total += potential[x];
|
|
1233
|
+
}
|
|
1234
|
+
const next = new Uint32Array(total);
|
|
1235
|
+
let k = 0;
|
|
1236
|
+
for (const x of touched) {
|
|
1237
|
+
next.fill(x, k, k + potential[x]);
|
|
1238
|
+
k += potential[x];
|
|
1239
|
+
potential[x] = 0;
|
|
1240
|
+
}
|
|
1241
|
+
stubs = next;
|
|
1242
|
+
}
|
|
1243
|
+
return stubs.length === 0;
|
|
1244
|
+
}
|
|
1245
|
+
function randomRegularGraph(options) {
|
|
1246
|
+
const { n, d } = options;
|
|
1247
|
+
const seed = resolveSeed(options.seed);
|
|
1248
|
+
checkInt("n", n, 0);
|
|
1249
|
+
checkInt("d", d, 0, Math.max(0, n - 1));
|
|
1250
|
+
if (n * d % 2 !== 0) {
|
|
1251
|
+
throw new RangeError(`n * d must be even, got ${n} * ${d}`);
|
|
1252
|
+
}
|
|
1253
|
+
checkEdgeCount(n * d / 2);
|
|
1254
|
+
const out = new EdgeBuffer(n * d / 2);
|
|
1255
|
+
if (d > 0) {
|
|
1256
|
+
const adj = new Uint32Array(n * d);
|
|
1257
|
+
const deg = new Uint32Array(n);
|
|
1258
|
+
const stream = new RandomStream(seed, "random-regular", 0);
|
|
1259
|
+
let attempt = 0;
|
|
1260
|
+
for (; attempt < REGULAR_ATTEMPTS; attempt++) {
|
|
1261
|
+
stream.reset(attempt);
|
|
1262
|
+
if (regularAttempt(n, d, stream, adj, deg)) {
|
|
1263
|
+
break;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (attempt === REGULAR_ATTEMPTS) {
|
|
1267
|
+
throw new RangeError(`randomRegularGraph found no ${d}-regular graph on ${n} nodes in ${REGULAR_ATTEMPTS} attempts`);
|
|
1268
|
+
}
|
|
1269
|
+
for (let u = 0; u < n; u++) {
|
|
1270
|
+
const row = adj.subarray(u * d, u * d + d).sort();
|
|
1271
|
+
for (const v of row) {
|
|
1272
|
+
if (v > u) {
|
|
1273
|
+
out.push(u, v);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
1279
|
+
}
|
|
1280
|
+
function checkPolicy(name, value) {
|
|
1281
|
+
if (value !== void 0 && value !== "keep" && value !== "erase") {
|
|
1282
|
+
throw new RangeError(`${name} must be "keep" or "erase", got ${String(value)}`);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
function finishRecursive(n, src, dst, options, seed, permuteDomain) {
|
|
1286
|
+
const directed = options.directed ?? true;
|
|
1287
|
+
const m = src.length;
|
|
1288
|
+
if (options.permute === true) {
|
|
1289
|
+
const perm = new Uint32Array(n);
|
|
1290
|
+
for (let i = 0; i < n; i++) {
|
|
1291
|
+
perm[i] = i;
|
|
1292
|
+
}
|
|
1293
|
+
const stream = new RandomStream(seed, permuteDomain, 0);
|
|
1294
|
+
for (let i = n - 1; i > 0; i--) {
|
|
1295
|
+
const j = stream.nextBelow(i + 1);
|
|
1296
|
+
const t = perm[i];
|
|
1297
|
+
perm[i] = perm[j];
|
|
1298
|
+
perm[j] = t;
|
|
1299
|
+
}
|
|
1300
|
+
for (let e = 0; e < m; e++) {
|
|
1301
|
+
src[e] = perm[src[e]];
|
|
1302
|
+
dst[e] = perm[dst[e]];
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
const keep = new Uint8Array(m).fill(1);
|
|
1306
|
+
if (options.selfLoops === "erase") {
|
|
1307
|
+
for (let e = 0; e < m; e++) {
|
|
1308
|
+
if (src[e] === dst[e]) {
|
|
1309
|
+
keep[e] = 0;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
if (options.multiEdges === "erase") {
|
|
1314
|
+
const lo = directed ? src : src.map((u, e) => Math.min(u, dst[e]));
|
|
1315
|
+
const hi = directed ? dst : src.map((u, e) => Math.max(u, dst[e]));
|
|
1316
|
+
const order = new Uint32Array(m);
|
|
1317
|
+
for (let e = 0; e < m; e++) {
|
|
1318
|
+
order[e] = e;
|
|
1319
|
+
}
|
|
1320
|
+
order.sort((a, b) => lo[a] - lo[b] || hi[a] - hi[b] || a - b);
|
|
1321
|
+
for (let i = 1; i < m; i++) {
|
|
1322
|
+
const a = order[i - 1];
|
|
1323
|
+
const b = order[i];
|
|
1324
|
+
if (lo[a] === lo[b] && hi[a] === hi[b]) {
|
|
1325
|
+
keep[b] = 0;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
const out = new EdgeBuffer(m);
|
|
1330
|
+
for (let e = 0; e < m; e++) {
|
|
1331
|
+
if (keep[e] === 1) {
|
|
1332
|
+
out.push(src[e], dst[e]);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return toGraph(n, out, directed);
|
|
1336
|
+
}
|
|
1337
|
+
function checkRecursive(options) {
|
|
1338
|
+
checkPolicy("selfLoops", options.selfLoops);
|
|
1339
|
+
checkPolicy("multiEdges", options.multiEdges);
|
|
1340
|
+
return resolveSeed(options.seed);
|
|
1341
|
+
}
|
|
1342
|
+
function kroneckerGraph(options) {
|
|
1343
|
+
const { initiator, power } = options;
|
|
1344
|
+
const k = initiator.length;
|
|
1345
|
+
checkInt("initiator.length", k, 1);
|
|
1346
|
+
let sum = 0;
|
|
1347
|
+
for (let r = 0; r < k; r++) {
|
|
1348
|
+
checkInt(`initiator[${r}].length`, initiator[r].length, k, k);
|
|
1349
|
+
for (const value of initiator[r]) {
|
|
1350
|
+
checkProbability(`initiator[${r}] entries`, value);
|
|
1351
|
+
sum += value;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
checkInt("power", power, 1, 64);
|
|
1355
|
+
let n = 1;
|
|
1356
|
+
let mean = 1;
|
|
1357
|
+
for (let i = 0; i < power; i++) {
|
|
1358
|
+
n *= k;
|
|
1359
|
+
mean *= sum;
|
|
1360
|
+
}
|
|
1361
|
+
checkInt("k^power (the node count)", n, 1);
|
|
1362
|
+
const m = options.edges ?? Math.round(mean);
|
|
1363
|
+
checkEdgeCount(m);
|
|
1364
|
+
checkInt("edges", m, 0);
|
|
1365
|
+
if (m > 0 && sum === 0) {
|
|
1366
|
+
throw new RangeError("edges must be 0 when the initiator is all zeros");
|
|
1367
|
+
}
|
|
1368
|
+
const seed = checkRecursive(options);
|
|
1369
|
+
const cells = k * k;
|
|
1370
|
+
const cumulative = new Float64Array(cells);
|
|
1371
|
+
let partial = 0;
|
|
1372
|
+
let lastPositive = 0;
|
|
1373
|
+
for (let i = 0; i < cells; i++) {
|
|
1374
|
+
const value = initiator[Math.floor(i / k)][i % k];
|
|
1375
|
+
partial += value;
|
|
1376
|
+
cumulative[i] = partial / sum;
|
|
1377
|
+
if (value > 0) {
|
|
1378
|
+
lastPositive = i;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
cumulative.fill(1, lastPositive);
|
|
1382
|
+
const src = new Uint32Array(m);
|
|
1383
|
+
const dst = new Uint32Array(m);
|
|
1384
|
+
const stream = new RandomStream(seed, "kronecker", 0);
|
|
1385
|
+
for (let e = 0; e < m; e++) {
|
|
1386
|
+
stream.reset(e);
|
|
1387
|
+
let u = 0;
|
|
1388
|
+
let v = 0;
|
|
1389
|
+
for (let level = 0; level < power; level++) {
|
|
1390
|
+
const x = stream.nextFloat();
|
|
1391
|
+
let cell = 0;
|
|
1392
|
+
while (x >= cumulative[cell]) {
|
|
1393
|
+
cell++;
|
|
1394
|
+
}
|
|
1395
|
+
u = u * k + Math.floor(cell / k);
|
|
1396
|
+
v = v * k + cell % k;
|
|
1397
|
+
}
|
|
1398
|
+
src[e] = u;
|
|
1399
|
+
dst[e] = v;
|
|
1400
|
+
}
|
|
1401
|
+
return applyWeights(finishRecursive(n, src, dst, options, seed, "kronecker-permute"), options);
|
|
1402
|
+
}
|
|
1403
|
+
function rmatGraph(options) {
|
|
1404
|
+
const { scale: scale2 } = options;
|
|
1405
|
+
const edgeFactor = options.edgeFactor ?? 16;
|
|
1406
|
+
const a = options.a ?? 0.57;
|
|
1407
|
+
const b = options.b ?? 0.19;
|
|
1408
|
+
const c = options.c ?? 0.19;
|
|
1409
|
+
const d = options.d ?? 0.05;
|
|
1410
|
+
checkInt("scale", scale2, 0, 31);
|
|
1411
|
+
checkInt("edgeFactor", edgeFactor, 0);
|
|
1412
|
+
checkProbability("a", a);
|
|
1413
|
+
checkProbability("b", b);
|
|
1414
|
+
checkProbability("c", c);
|
|
1415
|
+
checkProbability("d", d);
|
|
1416
|
+
if (Math.abs(a + b + c + d - 1) > 1e-9) {
|
|
1417
|
+
throw new RangeError(`a + b + c + d must be 1, got ${a + b + c + d}`);
|
|
1418
|
+
}
|
|
1419
|
+
const n = 2 ** scale2;
|
|
1420
|
+
const m = edgeFactor * n;
|
|
1421
|
+
checkEdgeCount(m);
|
|
1422
|
+
const seed = checkRecursive(options);
|
|
1423
|
+
const ab = a + b;
|
|
1424
|
+
const abc = a + b + c;
|
|
1425
|
+
const src = new Uint32Array(m);
|
|
1426
|
+
const dst = new Uint32Array(m);
|
|
1427
|
+
const stream = new RandomStream(seed, "rmat", 0);
|
|
1428
|
+
for (let e = 0; e < m; e++) {
|
|
1429
|
+
stream.reset(e);
|
|
1430
|
+
let u = 0;
|
|
1431
|
+
let v = 0;
|
|
1432
|
+
for (let level = 0; level < scale2; level++) {
|
|
1433
|
+
const x = stream.nextFloat();
|
|
1434
|
+
u *= 2;
|
|
1435
|
+
v *= 2;
|
|
1436
|
+
if (x < a) ;
|
|
1437
|
+
else if (x < ab) {
|
|
1438
|
+
v += 1;
|
|
1439
|
+
} else if (x < abc) {
|
|
1440
|
+
u += 1;
|
|
1441
|
+
} else {
|
|
1442
|
+
u += 1;
|
|
1443
|
+
v += 1;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
src[e] = u;
|
|
1447
|
+
dst[e] = v;
|
|
1448
|
+
}
|
|
1449
|
+
return applyWeights(finishRecursive(n, src, dst, options, seed, "rmat-permute"), options);
|
|
1450
|
+
}
|
|
1451
|
+
function priceGraph(options) {
|
|
1452
|
+
const { n, citations: m } = options;
|
|
1453
|
+
const attractiveness = options.attractiveness ?? 1;
|
|
1454
|
+
checkInt("n", n, 1);
|
|
1455
|
+
checkInt("citations", m, 1);
|
|
1456
|
+
if (!(attractiveness > 0 && Number.isFinite(attractiveness))) {
|
|
1457
|
+
throw new RangeError(`attractiveness must be a positive number, got ${String(attractiveness)}`);
|
|
1458
|
+
}
|
|
1459
|
+
const seed = resolveSeed(options.seed);
|
|
1460
|
+
const full = Math.min(m, n);
|
|
1461
|
+
const edgeCount = full * (full - 1) / 2 + (n - full) * m;
|
|
1462
|
+
checkEdgeCount(edgeCount);
|
|
1463
|
+
const stream = new RandomStream(seed, "price", 0);
|
|
1464
|
+
const out = new EdgeBuffer(edgeCount);
|
|
1465
|
+
const citedBy = new Int32Array(n).fill(-1);
|
|
1466
|
+
for (let t = 1; t < n; t++) {
|
|
1467
|
+
const arcs = out.length;
|
|
1468
|
+
if (t <= m) {
|
|
1469
|
+
for (let v = 0; v < t; v++) {
|
|
1470
|
+
out.push(t, v);
|
|
1471
|
+
}
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
const total = arcs + attractiveness * t;
|
|
1475
|
+
for (let found = 0; found < m; ) {
|
|
1476
|
+
const x = stream.nextFloat() * total;
|
|
1477
|
+
const v = x < arcs ? out.dst[stream.nextBelow(arcs)] : stream.nextBelow(t);
|
|
1478
|
+
if (citedBy[v] !== t) {
|
|
1479
|
+
citedBy[v] = t;
|
|
1480
|
+
out.push(t, v);
|
|
1481
|
+
found++;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return applyWeights(toGraph(n, out, true), options);
|
|
1486
|
+
}
|
|
1487
|
+
function orderDagRows(options, start, end, out) {
|
|
1488
|
+
const { n, p } = options;
|
|
1489
|
+
const logQ = detLog(1 - p);
|
|
1490
|
+
const stream = new RandomStream(resolveSeed(options.seed), "order-dag", 0);
|
|
1491
|
+
for (let u = start; u < end; u++) {
|
|
1492
|
+
stream.reset(u);
|
|
1493
|
+
bernoulliSegment(stream, p, logQ, u, u + 1, n, out);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
function randomOrderDagGraph(options) {
|
|
1497
|
+
const { n, p } = options;
|
|
1498
|
+
checkInt("n", n, 0);
|
|
1499
|
+
checkProbability("p", p);
|
|
1500
|
+
resolveSeed(options.seed);
|
|
1501
|
+
const out = binomialBuffer(n * (n - 1) / 2, p);
|
|
1502
|
+
orderDagRows(options, 0, n, out);
|
|
1503
|
+
return applyWeights(toGraph(n, out, true), options);
|
|
1504
|
+
}
|
|
1505
|
+
function erdosRenyiRows(options, start, end, out) {
|
|
1506
|
+
const { n, p } = options;
|
|
1507
|
+
const logQ = detLog(1 - p);
|
|
1508
|
+
if (options.directed === true) {
|
|
1509
|
+
directedRows(n, p, logQ, resolveSeed(options.seed), start, end, out);
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
const stream = new RandomStream(resolveSeed(options.seed), "gnp", 0);
|
|
1513
|
+
for (let u = start; u < end; u++) {
|
|
1514
|
+
stream.reset(u);
|
|
1515
|
+
bernoulliSegment(stream, p, logQ, u, u + 1, n, out);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
function directedRows(n, p, logQ, seed, start, end, out) {
|
|
1519
|
+
if (p === 0) {
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const stream = new RandomStream(seed, "gnp-directed", 0);
|
|
1523
|
+
for (let u = start; u < end; u++) {
|
|
1524
|
+
stream.reset(u);
|
|
1525
|
+
let j = p === 1 ? 0 : stream.nextSkip(logQ);
|
|
1526
|
+
while (j < n - 1) {
|
|
1527
|
+
out.push(u, j < u ? j : j + 1);
|
|
1528
|
+
j += p === 1 ? 1 : 1 + stream.nextSkip(logQ);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function erdosRenyiGraph(options) {
|
|
1533
|
+
const { n, p } = options;
|
|
1534
|
+
checkInt("n", n, 0);
|
|
1535
|
+
checkProbability("p", p);
|
|
1536
|
+
resolveSeed(options.seed);
|
|
1537
|
+
const directed = options.directed === true;
|
|
1538
|
+
const pairs = n * (n - 1) / (directed ? 1 : 2);
|
|
1539
|
+
const out = binomialBuffer(pairs, p);
|
|
1540
|
+
erdosRenyiRows(options, 0, n, out);
|
|
1541
|
+
return applyWeights(toGraph(n, out, directed), options);
|
|
1542
|
+
}
|
|
1543
|
+
class NumberSet {
|
|
1544
|
+
/**
|
|
1545
|
+
* An empty set.
|
|
1546
|
+
* @param capacity - the maximum number of entries
|
|
1547
|
+
*/
|
|
1548
|
+
constructor(capacity) {
|
|
1549
|
+
let size = 16;
|
|
1550
|
+
while (size < capacity * 2) {
|
|
1551
|
+
size *= 2;
|
|
1552
|
+
}
|
|
1553
|
+
this.slots = new Float64Array(size).fill(-1);
|
|
1554
|
+
this.mask = size - 1;
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Insert `x`.
|
|
1558
|
+
* @param x - the value
|
|
1559
|
+
* @returns false when it was already present
|
|
1560
|
+
*/
|
|
1561
|
+
add(x) {
|
|
1562
|
+
let i = Math.imul(x >>> 0 ^ Math.floor(x / 4294967296), 2654435761) & this.mask;
|
|
1563
|
+
for (; ; ) {
|
|
1564
|
+
const slot = this.slots[i];
|
|
1565
|
+
if (slot === -1) {
|
|
1566
|
+
this.slots[i] = x;
|
|
1567
|
+
return true;
|
|
1568
|
+
}
|
|
1569
|
+
if (slot === x) {
|
|
1570
|
+
return false;
|
|
1571
|
+
}
|
|
1572
|
+
i = i + 1 & this.mask;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
function erdosRenyiGnmGraph(options) {
|
|
1577
|
+
const { n, m } = options;
|
|
1578
|
+
const seed = resolveSeed(options.seed);
|
|
1579
|
+
checkInt("n", n, 0, 2 ** 27);
|
|
1580
|
+
const total = n * (n - 1) / 2;
|
|
1581
|
+
checkInt("m", m, 0, Math.min(total, 2 ** 32 - 2));
|
|
1582
|
+
const stream = new RandomStream(seed, "gnm", 0);
|
|
1583
|
+
const chosen = new Float64Array(m);
|
|
1584
|
+
const set = new NumberSet(m);
|
|
1585
|
+
for (let k = 0, j = total - m; j < total; j++, k++) {
|
|
1586
|
+
const t = stream.nextBelow(j + 1);
|
|
1587
|
+
if (set.add(t)) {
|
|
1588
|
+
chosen[k] = t;
|
|
1589
|
+
} else {
|
|
1590
|
+
set.add(j);
|
|
1591
|
+
chosen[k] = j;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
chosen.sort();
|
|
1595
|
+
const out = new EdgeBuffer(m);
|
|
1596
|
+
let u = 0;
|
|
1597
|
+
let rowStart = 0;
|
|
1598
|
+
for (let k = 0; k < m; k++) {
|
|
1599
|
+
const index = chosen[k];
|
|
1600
|
+
while (index >= rowStart + (n - u - 1)) {
|
|
1601
|
+
rowStart += n - u - 1;
|
|
1602
|
+
u++;
|
|
1603
|
+
}
|
|
1604
|
+
out.push(u, u + 1 + (index - rowStart));
|
|
1605
|
+
}
|
|
1606
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
1607
|
+
}
|
|
1608
|
+
const MAX_CAPACITY = 2 ** 24;
|
|
1609
|
+
function capacityRange(options, terminalFactor) {
|
|
1610
|
+
const min = options.minCapacity ?? 1;
|
|
1611
|
+
const max = options.maxCapacity ?? 10;
|
|
1612
|
+
checkInt("minCapacity", min, 0, MAX_CAPACITY);
|
|
1613
|
+
checkInt("maxCapacity", max, min, Math.floor(MAX_CAPACITY / terminalFactor));
|
|
1614
|
+
return { min, max };
|
|
1615
|
+
}
|
|
1616
|
+
function toNetwork(n, out, capacities, source, sink, columns) {
|
|
1617
|
+
const { src, dst } = out.finish();
|
|
1618
|
+
const role = new Uint8Array(n);
|
|
1619
|
+
role[source] = 1;
|
|
1620
|
+
role[sink] = 2;
|
|
1621
|
+
return {
|
|
1622
|
+
directed: true,
|
|
1623
|
+
nodeCount: n,
|
|
1624
|
+
src,
|
|
1625
|
+
dst,
|
|
1626
|
+
weights: Float32Array.from(capacities),
|
|
1627
|
+
nodeColumns: { ...columns, role },
|
|
1628
|
+
source,
|
|
1629
|
+
sink
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
function gridArcs(out, base, rows, cols, r, c) {
|
|
1633
|
+
const u = base + r * cols + c;
|
|
1634
|
+
const before = out.length;
|
|
1635
|
+
if (c + 1 < cols) {
|
|
1636
|
+
out.push(u, u + 1);
|
|
1637
|
+
}
|
|
1638
|
+
if (r + 1 < rows) {
|
|
1639
|
+
out.push(u, u + cols);
|
|
1640
|
+
}
|
|
1641
|
+
if (c > 0) {
|
|
1642
|
+
out.push(u, u - 1);
|
|
1643
|
+
}
|
|
1644
|
+
if (r > 0) {
|
|
1645
|
+
out.push(u, u - cols);
|
|
1646
|
+
}
|
|
1647
|
+
return out.length - before;
|
|
1648
|
+
}
|
|
1649
|
+
function gridFlowNetwork(options) {
|
|
1650
|
+
const { rows, cols } = options;
|
|
1651
|
+
checkInt("rows", rows, 1);
|
|
1652
|
+
checkInt("cols", cols, 2);
|
|
1653
|
+
const cells = rows * cols;
|
|
1654
|
+
checkInt("rows * cols + 2 (the node count)", cells + 2, 0);
|
|
1655
|
+
const { min, max } = capacityRange(options, 4);
|
|
1656
|
+
const seed = resolveSeed(options.seed);
|
|
1657
|
+
const arcs = 2 * (rows * (cols - 1) + cols * (rows - 1)) + 2 * rows;
|
|
1658
|
+
checkEdgeCount(arcs);
|
|
1659
|
+
const out = new EdgeBuffer(arcs);
|
|
1660
|
+
const capacities = [];
|
|
1661
|
+
const stream = new RandomStream(seed, "grid-flow", 0);
|
|
1662
|
+
const x = new Float64Array(cells + 2);
|
|
1663
|
+
const y = new Float64Array(cells + 2);
|
|
1664
|
+
for (let r = 0; r < rows; r++) {
|
|
1665
|
+
for (let c = 0; c < cols; c++) {
|
|
1666
|
+
const u = r * cols + c;
|
|
1667
|
+
x[u] = c;
|
|
1668
|
+
y[u] = r;
|
|
1669
|
+
stream.reset(u);
|
|
1670
|
+
const count = gridArcs(out, 0, rows, cols, r, c);
|
|
1671
|
+
for (let i = 0; i < count; i++) {
|
|
1672
|
+
capacities.push(min + stream.nextBelow(max - min + 1));
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const source = cells;
|
|
1677
|
+
const sink = cells + 1;
|
|
1678
|
+
x[source] = -1;
|
|
1679
|
+
x[sink] = cols;
|
|
1680
|
+
y[source] = (rows - 1) / 2;
|
|
1681
|
+
y[sink] = (rows - 1) / 2;
|
|
1682
|
+
for (let r = 0; r < rows; r++) {
|
|
1683
|
+
out.push(source, r * cols);
|
|
1684
|
+
capacities.push(4 * max);
|
|
1685
|
+
}
|
|
1686
|
+
for (let r = 0; r < rows; r++) {
|
|
1687
|
+
out.push(r * cols + cols - 1, sink);
|
|
1688
|
+
capacities.push(4 * max);
|
|
1689
|
+
}
|
|
1690
|
+
return toNetwork(cells + 2, out, capacities, source, sink, { x, y });
|
|
1691
|
+
}
|
|
1692
|
+
function planLayeredFlow(options) {
|
|
1693
|
+
const { layers, p } = options;
|
|
1694
|
+
checkInt("layers.length", layers.length, 1);
|
|
1695
|
+
checkProbability("p", p);
|
|
1696
|
+
const starts = [0];
|
|
1697
|
+
let widest = 0;
|
|
1698
|
+
for (let i = 0; i < layers.length; i++) {
|
|
1699
|
+
checkInt(`layers[${i}]`, layers[i], 1);
|
|
1700
|
+
starts.push(starts[i] + layers[i]);
|
|
1701
|
+
widest = Math.max(widest, layers[i]);
|
|
1702
|
+
}
|
|
1703
|
+
const inner = starts[layers.length];
|
|
1704
|
+
checkInt("the total size + 2 (the node count)", inner + 2, 0);
|
|
1705
|
+
const { min, max } = capacityRange(options, widest);
|
|
1706
|
+
const layerOf = new Uint32Array(inner);
|
|
1707
|
+
for (let i = 0; i < layers.length; i++) {
|
|
1708
|
+
layerOf.fill(i, starts[i], starts[i + 1]);
|
|
1709
|
+
}
|
|
1710
|
+
return { starts, layerOf, p, min, max, terminal: max * widest, seed: resolveSeed(options.seed) };
|
|
1711
|
+
}
|
|
1712
|
+
function layeredFlowRows(plan, start, end, out, capacities) {
|
|
1713
|
+
const { starts, layerOf, p, min, max } = plan;
|
|
1714
|
+
const logQ = detLog(1 - p);
|
|
1715
|
+
const stream = new RandomStream(plan.seed, "layered-flow", 0);
|
|
1716
|
+
const last = starts.length - 2;
|
|
1717
|
+
for (let u = start; u < end; u++) {
|
|
1718
|
+
const layer = layerOf[u];
|
|
1719
|
+
if (layer < last) {
|
|
1720
|
+
stream.reset(u);
|
|
1721
|
+
const before = out.length;
|
|
1722
|
+
bernoulliSegment(stream, p, logQ, u, starts[layer + 1], starts[layer + 2], out);
|
|
1723
|
+
for (let i = before; i < out.length; i++) {
|
|
1724
|
+
capacities.push(min + stream.nextBelow(max - min + 1));
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
function layeredFlowNetwork(options) {
|
|
1730
|
+
const plan = planLayeredFlow(options);
|
|
1731
|
+
const { starts } = plan;
|
|
1732
|
+
const L = starts.length - 1;
|
|
1733
|
+
const inner = starts[L];
|
|
1734
|
+
const out = new EdgeBuffer(inner);
|
|
1735
|
+
const capacities = [];
|
|
1736
|
+
layeredFlowRows(plan, 0, inner, out, capacities);
|
|
1737
|
+
const source = inner;
|
|
1738
|
+
const sink = inner + 1;
|
|
1739
|
+
const { terminal } = plan;
|
|
1740
|
+
for (let u = starts[0]; u < starts[1]; u++) {
|
|
1741
|
+
out.push(source, u);
|
|
1742
|
+
capacities.push(terminal);
|
|
1743
|
+
}
|
|
1744
|
+
for (let u = starts[L - 1]; u < starts[L]; u++) {
|
|
1745
|
+
out.push(u, sink);
|
|
1746
|
+
capacities.push(terminal);
|
|
1747
|
+
}
|
|
1748
|
+
const layer = new Uint32Array(inner + 2);
|
|
1749
|
+
for (let u = 0; u < inner; u++) {
|
|
1750
|
+
layer[u] = plan.layerOf[u] + 1;
|
|
1751
|
+
}
|
|
1752
|
+
layer[sink] = L + 1;
|
|
1753
|
+
return toNetwork(inner + 2, out, capacities, source, sink, { layer });
|
|
1754
|
+
}
|
|
1755
|
+
function genrmfGraph(options) {
|
|
1756
|
+
const { a, b, c1, c2 } = options;
|
|
1757
|
+
checkInt("a", a, 1);
|
|
1758
|
+
checkInt("b", b, 1);
|
|
1759
|
+
const side = a * a;
|
|
1760
|
+
checkInt("a * a * b (the node count)", side * b, 2);
|
|
1761
|
+
checkInt("c1", c1, 0, MAX_CAPACITY);
|
|
1762
|
+
checkInt("c2", c2, c1, MAX_CAPACITY);
|
|
1763
|
+
if (c2 * side > MAX_CAPACITY) {
|
|
1764
|
+
throw new RangeError(`c2 * a * a must be at most 2^24 to stay exact, got ${c2 * side}`);
|
|
1765
|
+
}
|
|
1766
|
+
const seed = resolveSeed(options.seed);
|
|
1767
|
+
const n = side * b;
|
|
1768
|
+
const arcs = b * 4 * a * (a - 1) + (b - 1) * side;
|
|
1769
|
+
checkEdgeCount(arcs);
|
|
1770
|
+
const out = new EdgeBuffer(arcs);
|
|
1771
|
+
const capacities = [];
|
|
1772
|
+
const frame = new Uint32Array(n);
|
|
1773
|
+
const perm = new Uint32Array(side);
|
|
1774
|
+
const stream = new RandomStream(seed, "genrmf", 0);
|
|
1775
|
+
for (let f = 0; f < b; f++) {
|
|
1776
|
+
const base = f * side;
|
|
1777
|
+
frame.fill(f, base, base + side);
|
|
1778
|
+
for (let r = 0; r < a; r++) {
|
|
1779
|
+
for (let c = 0; c < a; c++) {
|
|
1780
|
+
const count = gridArcs(out, base, a, a, r, c);
|
|
1781
|
+
for (let i = 0; i < count; i++) {
|
|
1782
|
+
capacities.push(c2 * side);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
if (f === b - 1) {
|
|
1787
|
+
break;
|
|
1788
|
+
}
|
|
1789
|
+
stream.reset(f);
|
|
1790
|
+
for (let i = 0; i < side; i++) {
|
|
1791
|
+
perm[i] = i;
|
|
1792
|
+
}
|
|
1793
|
+
for (let i = side - 1; i > 0; i--) {
|
|
1794
|
+
const j = stream.nextBelow(i + 1);
|
|
1795
|
+
const t = perm[i];
|
|
1796
|
+
perm[i] = perm[j];
|
|
1797
|
+
perm[j] = t;
|
|
1798
|
+
}
|
|
1799
|
+
for (let j = 0; j < side; j++) {
|
|
1800
|
+
out.push(base + j, base + side + perm[j]);
|
|
1801
|
+
capacities.push(c1 + stream.nextBelow(c2 - c1 + 1));
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
return toNetwork(n, out, capacities, 0, n - 1, { frame });
|
|
1805
|
+
}
|
|
1806
|
+
const AK_BIG = 1e6;
|
|
1807
|
+
function akGraph(options) {
|
|
1808
|
+
const { k } = options;
|
|
1809
|
+
checkInt("k", k, 1, 499998);
|
|
1810
|
+
const n = 4 * k + 6;
|
|
1811
|
+
const out = new EdgeBuffer(6 * k + 7);
|
|
1812
|
+
const capacities = [];
|
|
1813
|
+
const arc = (u, v, capacity) => {
|
|
1814
|
+
out.push(u, v);
|
|
1815
|
+
capacities.push(capacity);
|
|
1816
|
+
};
|
|
1817
|
+
const hub = k + 3;
|
|
1818
|
+
const chainEnd = 2 * k + 3;
|
|
1819
|
+
const b0 = 2 * k + 4;
|
|
1820
|
+
for (let i = 0; i < k; i++) {
|
|
1821
|
+
arc(2 + i, 3 + i, k + 1 - i);
|
|
1822
|
+
arc(2 + i, hub, 1);
|
|
1823
|
+
}
|
|
1824
|
+
arc(2 + k, chainEnd, 1);
|
|
1825
|
+
arc(2 + k, hub, 1);
|
|
1826
|
+
for (let j = hub; j < chainEnd; j++) {
|
|
1827
|
+
arc(j, j + 1, k + 1);
|
|
1828
|
+
}
|
|
1829
|
+
for (let i = 0; i <= 2 * k; i++) {
|
|
1830
|
+
arc(b0 + i, b0 + i + 1, k);
|
|
1831
|
+
}
|
|
1832
|
+
for (let i = 0; i < k; i++) {
|
|
1833
|
+
arc(b0 + i, b0 + 2 * k + 1 - i, 1);
|
|
1834
|
+
}
|
|
1835
|
+
arc(0, 2, AK_BIG);
|
|
1836
|
+
arc(0, b0, AK_BIG);
|
|
1837
|
+
arc(chainEnd, 1, AK_BIG);
|
|
1838
|
+
arc(b0 + 2 * k + 1, 1, AK_BIG);
|
|
1839
|
+
return toNetwork(n, out, capacities, 0, 1, {});
|
|
1840
|
+
}
|
|
1841
|
+
const WAXMAN_L = Math.SQRT2;
|
|
1842
|
+
const WAXMAN_MAX_DRAWS = 5e8;
|
|
1843
|
+
function checkDimension(dimension) {
|
|
1844
|
+
const d = dimension ?? 2;
|
|
1845
|
+
if (d !== 2 && d !== 3) {
|
|
1846
|
+
throw new RangeError(`dimension must be 2 or 3, got ${String(d)}`);
|
|
1847
|
+
}
|
|
1848
|
+
return d;
|
|
1849
|
+
}
|
|
1850
|
+
function checkNonNegative(name, value, positive = false) {
|
|
1851
|
+
if (!(Number.isFinite(value) && (positive ? value > 0 : value >= 0))) {
|
|
1852
|
+
throw new RangeError(`${name} must be a finite number ${positive ? ">" : ">="} 0, got ${String(value)}`);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
function uniformPoints(seed, domain, n, dimension) {
|
|
1856
|
+
const axes = Array.from({ length: dimension }, () => new Float64Array(n));
|
|
1857
|
+
const stream = new RandomStream(seed, domain, 0);
|
|
1858
|
+
for (let i = 0; i < n; i++) {
|
|
1859
|
+
stream.reset(i);
|
|
1860
|
+
for (const axis of axes) {
|
|
1861
|
+
axis[i] = stream.nextFloat();
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
return axes;
|
|
1865
|
+
}
|
|
1866
|
+
function positionColumns(axes) {
|
|
1867
|
+
const names = ["x", "y", "z"];
|
|
1868
|
+
return Object.fromEntries(axes.map((axis, a) => [names[a], axis]));
|
|
1869
|
+
}
|
|
1870
|
+
function cellsPerAxis(target, dimension) {
|
|
1871
|
+
let g = 1;
|
|
1872
|
+
while ((g + 1) ** dimension <= target) {
|
|
1873
|
+
g++;
|
|
1874
|
+
}
|
|
1875
|
+
return g;
|
|
1876
|
+
}
|
|
1877
|
+
function buildGrid(axes, g, lo, hi) {
|
|
1878
|
+
const n = axes[0].length;
|
|
1879
|
+
const side = lo.map((l, a) => (hi[a] - l) / g || 1);
|
|
1880
|
+
const cell = axes.map(() => new Uint32Array(n));
|
|
1881
|
+
const flat = new Uint32Array(n);
|
|
1882
|
+
const start = new Uint32Array(g ** axes.length + 1);
|
|
1883
|
+
for (let i = 0; i < n; i++) {
|
|
1884
|
+
let c = 0;
|
|
1885
|
+
for (let a = 0; a < axes.length; a++) {
|
|
1886
|
+
const k = Math.min(g - 1, Math.floor((axes[a][i] - lo[a]) / side[a]));
|
|
1887
|
+
cell[a][i] = k;
|
|
1888
|
+
c = c * g + k;
|
|
1889
|
+
}
|
|
1890
|
+
flat[i] = c;
|
|
1891
|
+
start[c + 1]++;
|
|
1892
|
+
}
|
|
1893
|
+
for (let c = 0; c + 1 < start.length; c++) {
|
|
1894
|
+
start[c + 1] += start[c];
|
|
1895
|
+
}
|
|
1896
|
+
const fill = start.slice(0, -1);
|
|
1897
|
+
const items = new Uint32Array(n);
|
|
1898
|
+
for (let i = 0; i < n; i++) {
|
|
1899
|
+
items[fill[flat[i]]++] = i;
|
|
1900
|
+
}
|
|
1901
|
+
return { g, lo, side, start, items, cell };
|
|
1902
|
+
}
|
|
1903
|
+
function randomGeometricGraph(options) {
|
|
1904
|
+
const { n, radius } = options;
|
|
1905
|
+
checkInt("n", n, 0);
|
|
1906
|
+
checkNonNegative("radius", radius);
|
|
1907
|
+
const dimension = checkDimension(options.dimension);
|
|
1908
|
+
const periodic = options.periodic ?? false;
|
|
1909
|
+
const seed = resolveSeed(options.seed);
|
|
1910
|
+
const axes = uniformPoints(seed, "rgg-points", n, dimension);
|
|
1911
|
+
const r2 = radius * radius;
|
|
1912
|
+
const g = Math.max(1, Math.min(Math.floor((1 - 1e-9) / radius), cellsPerAxis(Math.max(1, n), dimension)));
|
|
1913
|
+
const grid = buildGrid(axes, g, new Array(dimension).fill(0), new Array(dimension).fill(1));
|
|
1914
|
+
const volume = dimension === 2 ? Math.PI * r2 : 4 / 3 * Math.PI * r2 * radius;
|
|
1915
|
+
const pairs = n * (n - 1) / 2;
|
|
1916
|
+
const out = new EdgeBuffer(Math.ceil(Math.min(pairs, pairs * volume * 1.1, 1 << 24) + 16));
|
|
1917
|
+
const around = Array.from({ length: g }, (_, k) => {
|
|
1918
|
+
const list = periodic ? [(k + g - 1) % g, k, (k + 1) % g] : [k - 1, k, k + 1].filter((c) => c >= 0 && c < g);
|
|
1919
|
+
return [...new Set(list)];
|
|
1920
|
+
});
|
|
1921
|
+
let found = new Uint32Array(16);
|
|
1922
|
+
for (let u = 0; u < n; u++) {
|
|
1923
|
+
let count = 0;
|
|
1924
|
+
const zCells = dimension === 3 ? around[grid.cell[2][u]] : [0];
|
|
1925
|
+
for (const cx of around[grid.cell[0][u]]) {
|
|
1926
|
+
for (const cy of around[grid.cell[1][u]]) {
|
|
1927
|
+
for (const cz of zCells) {
|
|
1928
|
+
const c = dimension === 3 ? (cx * g + cy) * g + cz : cx * g + cy;
|
|
1929
|
+
for (let t = grid.start[c]; t < grid.start[c + 1]; t++) {
|
|
1930
|
+
const v = grid.items[t];
|
|
1931
|
+
if (v <= u) {
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
let d2 = 0;
|
|
1935
|
+
for (const axis of axes) {
|
|
1936
|
+
let d = Math.abs(axis[u] - axis[v]);
|
|
1937
|
+
if (periodic && d > 0.5) {
|
|
1938
|
+
d = 1 - d;
|
|
1939
|
+
}
|
|
1940
|
+
d2 += d * d;
|
|
1941
|
+
}
|
|
1942
|
+
if (d2 <= r2) {
|
|
1943
|
+
if (count === found.length) {
|
|
1944
|
+
const grown = new Uint32Array(count * 2);
|
|
1945
|
+
grown.set(found);
|
|
1946
|
+
found = grown;
|
|
1947
|
+
}
|
|
1948
|
+
found[count++] = v;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
const row = found.subarray(0, count).sort();
|
|
1955
|
+
for (const v of row) {
|
|
1956
|
+
out.push(u, v);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
return applyWeights(toGraph(n, out, false, positionColumns(axes)), options);
|
|
1960
|
+
}
|
|
1961
|
+
function planWaxman(options) {
|
|
1962
|
+
const { n, alpha, beta } = options;
|
|
1963
|
+
checkInt("n", n, 0);
|
|
1964
|
+
checkNonNegative("alpha", alpha, true);
|
|
1965
|
+
checkProbability("beta", beta);
|
|
1966
|
+
const seed = resolveSeed(options.seed);
|
|
1967
|
+
if (beta * n * n / 2 > WAXMAN_MAX_DRAWS) {
|
|
1968
|
+
throw new RangeError(
|
|
1969
|
+
`waxmanGraph: beta n^2 / 2 = ${beta * n * n / 2} candidate pairs, above the limit of ${WAXMAN_MAX_DRAWS}`
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
const [x, y] = uniformPoints(seed, "waxman-points", n, 2);
|
|
1973
|
+
return { n, seed, beta, logQ: detLog(1 - beta), scale: alpha * WAXMAN_L, x, y };
|
|
1974
|
+
}
|
|
1975
|
+
function waxmanRows(plan, start, end, out) {
|
|
1976
|
+
const { n, beta, logQ, scale: scale2, x, y } = plan;
|
|
1977
|
+
if (beta === 0) {
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
const stream = new RandomStream(plan.seed, "waxman", 0);
|
|
1981
|
+
const skip = () => beta === 1 ? 0 : stream.nextSkip(logQ);
|
|
1982
|
+
for (let u = start; u < end; u++) {
|
|
1983
|
+
stream.reset(u);
|
|
1984
|
+
for (let v = u + 1 + skip(); v < n; v += 1 + skip()) {
|
|
1985
|
+
const dx = x[u] - x[v];
|
|
1986
|
+
const dy = y[u] - y[v];
|
|
1987
|
+
const bound = scale2 * detLog(stream.nextFloat());
|
|
1988
|
+
if (dx * dx + dy * dy < bound * bound) {
|
|
1989
|
+
out.push(u, v);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
function waxmanGraph(options) {
|
|
1995
|
+
const plan = planWaxman(options);
|
|
1996
|
+
const out = new EdgeBuffer(Math.min(Math.ceil(plan.beta * plan.n * plan.n / 8), 1 << 24) + 16);
|
|
1997
|
+
waxmanRows(plan, 0, plan.n, out);
|
|
1998
|
+
return applyWeights(toGraph(plan.n, out, false, positionColumns([plan.x, plan.y])), options);
|
|
1999
|
+
}
|
|
2000
|
+
function mixturePoints(seed, n, dimension, clusters, spread) {
|
|
2001
|
+
const centres = uniformPoints(seed, "knn-centres", clusters, dimension);
|
|
2002
|
+
const axes = Array.from({ length: dimension }, () => new Float64Array(n));
|
|
2003
|
+
const community = new Uint32Array(n);
|
|
2004
|
+
const stream = new RandomStream(seed, "knn-points", 0);
|
|
2005
|
+
const normals = new Float64Array(4);
|
|
2006
|
+
for (let i = 0; i < n; i++) {
|
|
2007
|
+
stream.reset(i);
|
|
2008
|
+
const c = stream.nextBelow(clusters);
|
|
2009
|
+
community[i] = c;
|
|
2010
|
+
for (let j = 0; j < dimension; j += 2) {
|
|
2011
|
+
let u;
|
|
2012
|
+
let v;
|
|
2013
|
+
let s;
|
|
2014
|
+
do {
|
|
2015
|
+
u = 2 * stream.nextFloat() - 1;
|
|
2016
|
+
v = 2 * stream.nextFloat() - 1;
|
|
2017
|
+
s = u * u + v * v;
|
|
2018
|
+
} while (s >= 1 || s === 0);
|
|
2019
|
+
const m = Math.sqrt(-2 * detLog(s) / s);
|
|
2020
|
+
normals[j] = u * m;
|
|
2021
|
+
normals[j + 1] = v * m;
|
|
2022
|
+
}
|
|
2023
|
+
for (let a = 0; a < dimension; a++) {
|
|
2024
|
+
axes[a][i] = centres[a][c] + spread * normals[a];
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return { axes, community };
|
|
2028
|
+
}
|
|
2029
|
+
function nearestNeighbours(axes, k) {
|
|
2030
|
+
const n = axes[0].length;
|
|
2031
|
+
const dimension = axes.length;
|
|
2032
|
+
const result = new Uint32Array(n * k);
|
|
2033
|
+
if (k === 0) {
|
|
2034
|
+
return result;
|
|
2035
|
+
}
|
|
2036
|
+
const lo = axes.map((axis) => axis.reduce((a, b) => Math.min(a, b), Infinity));
|
|
2037
|
+
const hi = axes.map((axis) => axis.reduce((a, b) => Math.max(a, b), -Infinity));
|
|
2038
|
+
const g = cellsPerAxis(Math.max(1, n / k), dimension);
|
|
2039
|
+
const grid = buildGrid(axes, g, lo, hi);
|
|
2040
|
+
const bestD = new Float64Array(k);
|
|
2041
|
+
const bestI = new Uint32Array(k);
|
|
2042
|
+
const slack = 1e-12 * Math.max(1, ...lo.map(Math.abs), ...hi.map(Math.abs));
|
|
2043
|
+
const cell = new Array(dimension).fill(0);
|
|
2044
|
+
const offset = new Array(dimension).fill(0);
|
|
2045
|
+
for (let u = 0; u < n; u++) {
|
|
2046
|
+
let count = 0;
|
|
2047
|
+
const consider = (v) => {
|
|
2048
|
+
let d2 = 0;
|
|
2049
|
+
for (const axis of axes) {
|
|
2050
|
+
const d = Math.abs(axis[u] - axis[v]);
|
|
2051
|
+
d2 += d * d;
|
|
2052
|
+
}
|
|
2053
|
+
if (count === k && (d2 > bestD[k - 1] || d2 === bestD[k - 1] && v > bestI[k - 1])) {
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
let j = count < k ? count++ : k - 1;
|
|
2057
|
+
while (j > 0 && (bestD[j - 1] > d2 || bestD[j - 1] === d2 && bestI[j - 1] > v)) {
|
|
2058
|
+
bestD[j] = bestD[j - 1];
|
|
2059
|
+
bestI[j] = bestI[j - 1];
|
|
2060
|
+
j--;
|
|
2061
|
+
}
|
|
2062
|
+
bestD[j] = d2;
|
|
2063
|
+
bestI[j] = v;
|
|
2064
|
+
};
|
|
2065
|
+
for (let a = 0; a < dimension; a++) {
|
|
2066
|
+
cell[a] = grid.cell[a][u];
|
|
2067
|
+
}
|
|
2068
|
+
for (let r = 0; ; r++) {
|
|
2069
|
+
let covered = true;
|
|
2070
|
+
for (let a = 0; a < dimension; a++) {
|
|
2071
|
+
offset[a] = -r;
|
|
2072
|
+
if (cell[a] - r > 0 || cell[a] + r < g - 1) {
|
|
2073
|
+
covered = false;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
for (; ; ) {
|
|
2077
|
+
let chebyshev = 0;
|
|
2078
|
+
let inside = true;
|
|
2079
|
+
let c = 0;
|
|
2080
|
+
for (let a2 = 0; a2 < dimension; a2++) {
|
|
2081
|
+
const k2 = cell[a2] + offset[a2];
|
|
2082
|
+
if (k2 < 0 || k2 >= g) {
|
|
2083
|
+
inside = false;
|
|
2084
|
+
}
|
|
2085
|
+
chebyshev = Math.max(chebyshev, Math.abs(offset[a2]));
|
|
2086
|
+
c = c * g + k2;
|
|
2087
|
+
}
|
|
2088
|
+
if (inside && chebyshev === r) {
|
|
2089
|
+
for (let t = grid.start[c]; t < grid.start[c + 1]; t++) {
|
|
2090
|
+
const v = grid.items[t];
|
|
2091
|
+
if (v !== u) {
|
|
2092
|
+
consider(v);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
let a = dimension - 1;
|
|
2097
|
+
while (a >= 0 && offset[a] === r) {
|
|
2098
|
+
offset[a] = -r;
|
|
2099
|
+
a--;
|
|
2100
|
+
}
|
|
2101
|
+
if (a < 0) {
|
|
2102
|
+
break;
|
|
2103
|
+
}
|
|
2104
|
+
offset[a]++;
|
|
2105
|
+
}
|
|
2106
|
+
if (covered) {
|
|
2107
|
+
break;
|
|
2108
|
+
}
|
|
2109
|
+
if (count === k) {
|
|
2110
|
+
let gap = Infinity;
|
|
2111
|
+
for (let a = 0; a < dimension; a++) {
|
|
2112
|
+
const p = axes[a][u] - grid.lo[a];
|
|
2113
|
+
gap = Math.min(gap, p - (cell[a] - r) * grid.side[a], (cell[a] + r + 1) * grid.side[a] - p);
|
|
2114
|
+
}
|
|
2115
|
+
gap -= slack;
|
|
2116
|
+
if (gap > 0 && bestD[k - 1] < gap * gap * (1 - 1e-9)) {
|
|
2117
|
+
break;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
const row = result.subarray(u * k, (u + 1) * k);
|
|
2122
|
+
row.set(bestI);
|
|
2123
|
+
row.sort();
|
|
2124
|
+
}
|
|
2125
|
+
return result;
|
|
2126
|
+
}
|
|
2127
|
+
function knnGraph(options) {
|
|
2128
|
+
const { n, k, clusters, spread } = options;
|
|
2129
|
+
checkInt("n", n, 0);
|
|
2130
|
+
checkInt("k", k, 0, Math.max(0, n - 1));
|
|
2131
|
+
const dimension = checkDimension(options.dimension);
|
|
2132
|
+
const directed = options.directed ?? true;
|
|
2133
|
+
if (clusters === void 0 && spread !== void 0) {
|
|
2134
|
+
throw new RangeError("spread needs clusters");
|
|
2135
|
+
}
|
|
2136
|
+
const seed = resolveSeed(options.seed);
|
|
2137
|
+
let axes;
|
|
2138
|
+
const columns = {};
|
|
2139
|
+
if (clusters === void 0) {
|
|
2140
|
+
axes = uniformPoints(seed, "knn-points", n, dimension);
|
|
2141
|
+
} else {
|
|
2142
|
+
checkInt("clusters", clusters, 1);
|
|
2143
|
+
const sd = spread ?? 0.05;
|
|
2144
|
+
checkNonNegative("spread", sd);
|
|
2145
|
+
let community;
|
|
2146
|
+
({ axes, community } = mixturePoints(seed, n, dimension, clusters, sd));
|
|
2147
|
+
columns.community = community;
|
|
2148
|
+
}
|
|
2149
|
+
Object.assign(columns, positionColumns(axes));
|
|
2150
|
+
const neighbours = nearestNeighbours(axes, k);
|
|
2151
|
+
checkEdgeCount(n * k);
|
|
2152
|
+
let out;
|
|
2153
|
+
if (directed) {
|
|
2154
|
+
out = new EdgeBuffer(n * k);
|
|
2155
|
+
for (let u = 0; u < n; u++) {
|
|
2156
|
+
for (let j = u * k; j < (u + 1) * k; j++) {
|
|
2157
|
+
out.push(u, neighbours[j]);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
} else {
|
|
2161
|
+
const start = new Uint32Array(n + 1);
|
|
2162
|
+
for (let u = 0; u < n; u++) {
|
|
2163
|
+
for (let j = u * k; j < (u + 1) * k; j++) {
|
|
2164
|
+
start[Math.min(u, neighbours[j]) + 1]++;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
for (let u = 0; u < n; u++) {
|
|
2168
|
+
start[u + 1] += start[u];
|
|
2169
|
+
}
|
|
2170
|
+
const fill = start.slice(0, -1);
|
|
2171
|
+
const other = new Uint32Array(n * k);
|
|
2172
|
+
for (let u = 0; u < n; u++) {
|
|
2173
|
+
for (let j = u * k; j < (u + 1) * k; j++) {
|
|
2174
|
+
const v = neighbours[j];
|
|
2175
|
+
other[fill[Math.min(u, v)]++] = Math.max(u, v);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
out = new EdgeBuffer(n * k);
|
|
2179
|
+
for (let u = 0; u < n; u++) {
|
|
2180
|
+
const row = other.subarray(start[u], start[u + 1]).sort();
|
|
2181
|
+
for (let j = 0; j < row.length; j++) {
|
|
2182
|
+
if (j === 0 || row[j] !== row[j - 1]) {
|
|
2183
|
+
out.push(u, row[j]);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
return applyWeights(toGraph(n, out, directed, columns), options);
|
|
2189
|
+
}
|
|
2190
|
+
function randomRecursiveTreeGraph(options) {
|
|
2191
|
+
const { n } = options;
|
|
2192
|
+
checkInt("n", n, 1);
|
|
2193
|
+
const stream = new RandomStream(resolveSeed(options.seed), "recursive-tree", 0);
|
|
2194
|
+
const src = new Uint32Array(n - 1);
|
|
2195
|
+
const dst = new Uint32Array(n - 1);
|
|
2196
|
+
for (let t = 1; t < n; t++) {
|
|
2197
|
+
src[t - 1] = stream.nextBelow(t);
|
|
2198
|
+
dst[t - 1] = t;
|
|
2199
|
+
}
|
|
2200
|
+
return applyWeights({ directed: false, nodeCount: n, src, dst }, options);
|
|
2201
|
+
}
|
|
2202
|
+
function pickPrefix(stream, candidates, count) {
|
|
2203
|
+
for (let i = 0; i < count; i++) {
|
|
2204
|
+
const j = i + stream.nextBelow(candidates.length - i);
|
|
2205
|
+
const t = candidates[i];
|
|
2206
|
+
candidates[i] = candidates[j];
|
|
2207
|
+
candidates[j] = t;
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
function forestFireGraph(options) {
|
|
2211
|
+
const { n, forward: p, backward: r } = options;
|
|
2212
|
+
const maxBurn = options.maxBurn ?? 1e3;
|
|
2213
|
+
checkInt("n", n, 1);
|
|
2214
|
+
if (!(p >= 0 && p < 1)) {
|
|
2215
|
+
throw new RangeError(`forward must be a probability in [0, 1), got ${String(p)}`);
|
|
2216
|
+
}
|
|
2217
|
+
if (!(r >= 0 && r * p < 1)) {
|
|
2218
|
+
throw new RangeError(`backward must be >= 0 with backward * forward < 1, got ${String(r)}`);
|
|
2219
|
+
}
|
|
2220
|
+
checkInt("maxBurn", maxBurn, 1);
|
|
2221
|
+
const stream = new RandomStream(resolveSeed(options.seed), "forest-fire", 0);
|
|
2222
|
+
const pb = r * p;
|
|
2223
|
+
const logForward = detLog(p);
|
|
2224
|
+
const logBackward = detLog(pb);
|
|
2225
|
+
const out = Array.from({ length: n }, () => []);
|
|
2226
|
+
const inn = Array.from({ length: n }, () => []);
|
|
2227
|
+
const stamp = new Uint32Array(n);
|
|
2228
|
+
const edges = new EdgeBuffer(4 * n);
|
|
2229
|
+
const burned = [];
|
|
2230
|
+
const burn = (v, list, count) => {
|
|
2231
|
+
const candidates = list.filter((t) => stamp[t] !== v);
|
|
2232
|
+
const take = Math.min(count, candidates.length, maxBurn - burned.length);
|
|
2233
|
+
pickPrefix(stream, candidates, take);
|
|
2234
|
+
for (let i = 0; i < take; i++) {
|
|
2235
|
+
stamp[candidates[i]] = v;
|
|
2236
|
+
burned.push(candidates[i]);
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
for (let v = 1; v < n; v++) {
|
|
2240
|
+
burned.length = 0;
|
|
2241
|
+
const w = stream.nextBelow(v);
|
|
2242
|
+
stamp[w] = v;
|
|
2243
|
+
burned.push(w);
|
|
2244
|
+
for (let head = 0; head < burned.length && burned.length < maxBurn; head++) {
|
|
2245
|
+
const x = burned[head];
|
|
2246
|
+
const xOut = p === 0 ? 0 : stream.nextSkip(logForward);
|
|
2247
|
+
const xIn = pb === 0 ? 0 : stream.nextSkip(logBackward);
|
|
2248
|
+
burn(v, out[x], xOut);
|
|
2249
|
+
burn(v, inn[x], xIn);
|
|
2250
|
+
}
|
|
2251
|
+
for (const t of burned) {
|
|
2252
|
+
edges.push(v, t);
|
|
2253
|
+
out[v].push(t);
|
|
2254
|
+
inn[t].push(v);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
return applyWeights(toGraph(n, edges, true), options);
|
|
2258
|
+
}
|
|
2259
|
+
function duplicationDivergenceGraph(options) {
|
|
2260
|
+
const { n, retention } = options;
|
|
2261
|
+
checkInt("n", n, 2);
|
|
2262
|
+
if (!(retention > 0 && retention <= 1)) {
|
|
2263
|
+
throw new RangeError(`retention must be a probability in (0, 1], got ${String(retention)}`);
|
|
2264
|
+
}
|
|
2265
|
+
const stream = new RandomStream(resolveSeed(options.seed), "duplication-divergence", 0);
|
|
2266
|
+
const adj = Array.from({ length: n }, () => []);
|
|
2267
|
+
const edges = new EdgeBuffer(4 * n);
|
|
2268
|
+
edges.push(0, 1);
|
|
2269
|
+
adj[0].push(1);
|
|
2270
|
+
adj[1].push(0);
|
|
2271
|
+
const maxAttempts = 1e3 * n;
|
|
2272
|
+
let attempts = 0;
|
|
2273
|
+
for (let i = 2; i < n; ) {
|
|
2274
|
+
if (++attempts > maxAttempts) {
|
|
2275
|
+
throw new RangeError(
|
|
2276
|
+
`duplicationDivergenceGraph gave up after ${maxAttempts} attempts at ${i} of ${n} nodes: retention ${retention} is too small`
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
2279
|
+
const copied = adj[stream.nextBelow(i)];
|
|
2280
|
+
for (const t of copied) {
|
|
2281
|
+
if (stream.nextFloat() < retention) {
|
|
2282
|
+
edges.push(i, t);
|
|
2283
|
+
adj[i].push(t);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
for (const t of adj[i]) {
|
|
2287
|
+
adj[t].push(i);
|
|
2288
|
+
}
|
|
2289
|
+
if (adj[i].length > 0) {
|
|
2290
|
+
i++;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
return applyWeights(toGraph(n, edges, false), options);
|
|
2294
|
+
}
|
|
2295
|
+
function newmanWattsGraph(options) {
|
|
2296
|
+
const { n, k, p } = options;
|
|
2297
|
+
checkInt("k", k, 2);
|
|
2298
|
+
if (k % 2 !== 0) {
|
|
2299
|
+
throw new RangeError(`k must be even, got ${k}`);
|
|
2300
|
+
}
|
|
2301
|
+
checkInt("n", n, k + 1);
|
|
2302
|
+
checkProbability("p", p);
|
|
2303
|
+
const half = k / 2;
|
|
2304
|
+
const ring = n * half;
|
|
2305
|
+
checkEdgeCount(2 * ring);
|
|
2306
|
+
const edges = new EdgeBuffer(ring + Math.ceil(ring * p + 6 * Math.sqrt(ring) + 16));
|
|
2307
|
+
const degree = new Uint32Array(n).fill(k);
|
|
2308
|
+
const present = /* @__PURE__ */ new Set();
|
|
2309
|
+
const key = (a, b) => a < b ? a * n + b : b * n + a;
|
|
2310
|
+
for (let u = 0; u < n; u++) {
|
|
2311
|
+
for (let j = 1; j <= half; j++) {
|
|
2312
|
+
const v = (u + j) % n;
|
|
2313
|
+
edges.push(u, v);
|
|
2314
|
+
present.add(key(u, v));
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
const stream = new RandomStream(resolveSeed(options.seed), "newman-watts", 0);
|
|
2318
|
+
for (let slot = 0; slot < ring; slot++) {
|
|
2319
|
+
const u = edges.src[slot];
|
|
2320
|
+
if (stream.nextFloat() >= p || degree[u] >= n - 1) {
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
let w = stream.nextBelow(n);
|
|
2324
|
+
while (w === u || present.has(key(u, w))) {
|
|
2325
|
+
w = stream.nextBelow(n);
|
|
2326
|
+
}
|
|
2327
|
+
edges.push(u, w);
|
|
2328
|
+
present.add(key(u, w));
|
|
2329
|
+
degree[u]++;
|
|
2330
|
+
degree[w]++;
|
|
2331
|
+
}
|
|
2332
|
+
return applyWeights(toGraph(n, edges, false), options);
|
|
2333
|
+
}
|
|
2334
|
+
function bianconiBarabasiGraph(options) {
|
|
2335
|
+
const { n, m } = options;
|
|
2336
|
+
checkInt("m", m, 1);
|
|
2337
|
+
checkInt("n", n, m + 1);
|
|
2338
|
+
const seed = resolveSeed(options.seed);
|
|
2339
|
+
checkEdgeCount(m * (m + 1) / 2 + (n - m - 1) * m);
|
|
2340
|
+
const fitness = new Float64Array(n);
|
|
2341
|
+
if (options.fitness === void 0) {
|
|
2342
|
+
const draw = new RandomStream(seed, "fitness", 0);
|
|
2343
|
+
for (let i = 0; i < n; i++) {
|
|
2344
|
+
draw.reset(i);
|
|
2345
|
+
fitness[i] = 1 - draw.nextFloat();
|
|
2346
|
+
}
|
|
2347
|
+
} else {
|
|
2348
|
+
if (options.fitness.length !== n) {
|
|
2349
|
+
throw new RangeError(`fitness must have n = ${n} values, got ${options.fitness.length}`);
|
|
2350
|
+
}
|
|
2351
|
+
for (let i = 0; i < n; i++) {
|
|
2352
|
+
const f = options.fitness[i];
|
|
2353
|
+
if (!(f > 0 && Number.isFinite(f))) {
|
|
2354
|
+
throw new RangeError(`fitness[${i}] must be finite and positive, got ${String(f)}`);
|
|
2355
|
+
}
|
|
2356
|
+
fitness[i] = f;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
const edges = new EdgeBuffer(m * (m + 1) / 2 + (n - m - 1) * m);
|
|
2360
|
+
const degree = new Uint32Array(n);
|
|
2361
|
+
for (let a = 0; a <= m; a++) {
|
|
2362
|
+
for (let b = a + 1; b <= m; b++) {
|
|
2363
|
+
edges.push(a, b);
|
|
2364
|
+
}
|
|
2365
|
+
degree[a] = m;
|
|
2366
|
+
}
|
|
2367
|
+
const weight = new Float64Array(n);
|
|
2368
|
+
const tree = new Float64Array(n + 1);
|
|
2369
|
+
const add = (i, delta) => {
|
|
2370
|
+
weight[i] += delta;
|
|
2371
|
+
for (let j = i + 1; j <= n; j += j & -j) {
|
|
2372
|
+
tree[j] += delta;
|
|
2373
|
+
}
|
|
2374
|
+
};
|
|
2375
|
+
let top = 1;
|
|
2376
|
+
while (top * 2 <= n) {
|
|
2377
|
+
top *= 2;
|
|
2378
|
+
}
|
|
2379
|
+
const find = (x, limit) => {
|
|
2380
|
+
let pos = 0;
|
|
2381
|
+
let rest = x;
|
|
2382
|
+
for (let step = top; step >= 1; step = Math.floor(step / 2)) {
|
|
2383
|
+
if (pos + step <= n && tree[pos + step] <= rest) {
|
|
2384
|
+
pos += step;
|
|
2385
|
+
rest -= tree[pos];
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
pos = Math.min(pos, limit - 1);
|
|
2389
|
+
while (weight[pos] === 0) {
|
|
2390
|
+
pos--;
|
|
2391
|
+
}
|
|
2392
|
+
return pos;
|
|
2393
|
+
};
|
|
2394
|
+
let total = 0;
|
|
2395
|
+
for (let i = 0; i <= m; i++) {
|
|
2396
|
+
add(i, fitness[i] * m);
|
|
2397
|
+
total += fitness[i] * m;
|
|
2398
|
+
}
|
|
2399
|
+
const stream = new RandomStream(seed, "bianconi-barabasi", 0);
|
|
2400
|
+
const picked = new Uint32Array(m);
|
|
2401
|
+
for (let s = m + 1; s < n; s++) {
|
|
2402
|
+
let remaining = total;
|
|
2403
|
+
for (let i = 0; i < m; i++) {
|
|
2404
|
+
const t = find(stream.nextFloat() * remaining, s);
|
|
2405
|
+
picked[i] = t;
|
|
2406
|
+
remaining -= weight[t];
|
|
2407
|
+
add(t, -weight[t]);
|
|
2408
|
+
}
|
|
2409
|
+
for (let i = 0; i < m; i++) {
|
|
2410
|
+
const t = picked[i];
|
|
2411
|
+
edges.push(s, t);
|
|
2412
|
+
degree[t]++;
|
|
2413
|
+
add(t, fitness[t] * degree[t]);
|
|
2414
|
+
}
|
|
2415
|
+
degree[s] = m;
|
|
2416
|
+
add(s, fitness[s] * m);
|
|
2417
|
+
total = 0;
|
|
2418
|
+
for (let j = n; j > 0; j -= j & -j) {
|
|
2419
|
+
total += tree[j];
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
return applyWeights(toGraph(n, edges, false, { fitness }), options);
|
|
2423
|
+
}
|
|
2424
|
+
function randomApollonianGraph(options) {
|
|
2425
|
+
const { n } = options;
|
|
2426
|
+
checkInt("n", n, 3);
|
|
2427
|
+
const m = 3 * n - 6;
|
|
2428
|
+
checkEdgeCount(m);
|
|
2429
|
+
const stream = new RandomStream(resolveSeed(options.seed), "apollonian", 0);
|
|
2430
|
+
const faces = 2 * n - 5;
|
|
2431
|
+
const fa = new Uint32Array(faces);
|
|
2432
|
+
const fb = new Uint32Array(faces);
|
|
2433
|
+
const fc = new Uint32Array(faces);
|
|
2434
|
+
fa[0] = 0;
|
|
2435
|
+
fb[0] = 1;
|
|
2436
|
+
fc[0] = 2;
|
|
2437
|
+
let faceCount = 1;
|
|
2438
|
+
const src = new Uint32Array(m);
|
|
2439
|
+
const dst = new Uint32Array(m);
|
|
2440
|
+
src.set([0, 0, 1]);
|
|
2441
|
+
dst.set([1, 2, 2]);
|
|
2442
|
+
let e = 3;
|
|
2443
|
+
for (let v = 3; v < n; v++) {
|
|
2444
|
+
const f = stream.nextBelow(faceCount);
|
|
2445
|
+
const a = fa[f];
|
|
2446
|
+
const b = fb[f];
|
|
2447
|
+
const c = fc[f];
|
|
2448
|
+
src.fill(v, e, e + 3);
|
|
2449
|
+
dst[e++] = a;
|
|
2450
|
+
dst[e++] = b;
|
|
2451
|
+
dst[e++] = c;
|
|
2452
|
+
fc[f] = v;
|
|
2453
|
+
fa[faceCount] = b;
|
|
2454
|
+
fb[faceCount] = c;
|
|
2455
|
+
fc[faceCount++] = v;
|
|
2456
|
+
fa[faceCount] = a;
|
|
2457
|
+
fb[faceCount] = c;
|
|
2458
|
+
fc[faceCount++] = v;
|
|
2459
|
+
}
|
|
2460
|
+
return applyWeights({ directed: false, nodeCount: n, src, dst }, options);
|
|
2461
|
+
}
|
|
2462
|
+
function wilsonMazeGraph(options) {
|
|
2463
|
+
const { rows, cols } = options;
|
|
2464
|
+
checkInt("rows", rows, 1);
|
|
2465
|
+
checkInt("cols", cols, 1);
|
|
2466
|
+
const n = rows * cols;
|
|
2467
|
+
checkInt("rows * cols", n, 1);
|
|
2468
|
+
const stream = new RandomStream(resolveSeed(options.seed), "wilson", 0);
|
|
2469
|
+
const inTree = new Uint8Array(n);
|
|
2470
|
+
const next = new Uint32Array(n);
|
|
2471
|
+
const src = new Uint32Array(n - 1);
|
|
2472
|
+
const dst = new Uint32Array(n - 1);
|
|
2473
|
+
const options4 = new Uint32Array(4);
|
|
2474
|
+
inTree[0] = 1;
|
|
2475
|
+
let e = 0;
|
|
2476
|
+
for (let start = 1; start < n; start++) {
|
|
2477
|
+
let u = start;
|
|
2478
|
+
while (inTree[u] === 0) {
|
|
2479
|
+
const r = Math.floor(u / cols);
|
|
2480
|
+
const c = u - r * cols;
|
|
2481
|
+
let count = 0;
|
|
2482
|
+
if (c > 0) {
|
|
2483
|
+
options4[count++] = u - 1;
|
|
2484
|
+
}
|
|
2485
|
+
if (c + 1 < cols) {
|
|
2486
|
+
options4[count++] = u + 1;
|
|
2487
|
+
}
|
|
2488
|
+
if (r > 0) {
|
|
2489
|
+
options4[count++] = u - cols;
|
|
2490
|
+
}
|
|
2491
|
+
if (r + 1 < rows) {
|
|
2492
|
+
options4[count++] = u + cols;
|
|
2493
|
+
}
|
|
2494
|
+
next[u] = options4[stream.nextBelow(count)];
|
|
2495
|
+
u = next[u];
|
|
2496
|
+
}
|
|
2497
|
+
for (u = start; inTree[u] === 0; u = next[u]) {
|
|
2498
|
+
inTree[u] = 1;
|
|
2499
|
+
src[e] = u;
|
|
2500
|
+
dst[e++] = next[u];
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
const x = new Float64Array(n);
|
|
2504
|
+
const y = new Float64Array(n);
|
|
2505
|
+
for (let i = 0; i < n; i++) {
|
|
2506
|
+
y[i] = Math.floor(i / cols);
|
|
2507
|
+
x[i] = i - y[i] * cols;
|
|
2508
|
+
}
|
|
2509
|
+
return applyWeights({ directed: false, nodeCount: n, src, dst, nodeColumns: { x, y } }, options);
|
|
2510
|
+
}
|
|
2511
|
+
const PIO4 = 0.7853981633974483;
|
|
2512
|
+
const S1 = -0.16666666666666632;
|
|
2513
|
+
const S2 = 0.00833333333332249;
|
|
2514
|
+
const S3 = -1984126982985795e-19;
|
|
2515
|
+
const S4 = 27557313707070068e-22;
|
|
2516
|
+
const S5 = -25050760253406863e-24;
|
|
2517
|
+
const S6 = 158969099521155e-24;
|
|
2518
|
+
const C1 = 0.0416666666666666;
|
|
2519
|
+
const C2 = -0.001388888888887411;
|
|
2520
|
+
const C3 = 2480158728947673e-20;
|
|
2521
|
+
const C4 = -27557314351390663e-23;
|
|
2522
|
+
const C5 = 2087572321298175e-24;
|
|
2523
|
+
const C6 = -11359647557788195e-27;
|
|
2524
|
+
function kernelSin(x) {
|
|
2525
|
+
const z = x * x;
|
|
2526
|
+
const r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)));
|
|
2527
|
+
return x + z * x * (S1 + z * r);
|
|
2528
|
+
}
|
|
2529
|
+
function kernelCos(x) {
|
|
2530
|
+
const z = x * x;
|
|
2531
|
+
const w = z * z;
|
|
2532
|
+
const r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6));
|
|
2533
|
+
const hz = 0.5 * z;
|
|
2534
|
+
const v = 1 - hz;
|
|
2535
|
+
return v + (1 - v - hz + z * r);
|
|
2536
|
+
}
|
|
2537
|
+
function detCosSinTurn(u, out) {
|
|
2538
|
+
const t = u * 8;
|
|
2539
|
+
const octant = Math.floor(t);
|
|
2540
|
+
const x = (t - octant) * PIO4;
|
|
2541
|
+
const odd = (octant & 1) === 1;
|
|
2542
|
+
const z = odd ? PIO4 - x : x;
|
|
2543
|
+
const c = odd ? kernelSin(z) : kernelCos(z);
|
|
2544
|
+
const s = odd ? kernelCos(z) : kernelSin(z);
|
|
2545
|
+
switch (octant >> 1 & 3) {
|
|
2546
|
+
case 0:
|
|
2547
|
+
out[0] = c;
|
|
2548
|
+
out[1] = s;
|
|
2549
|
+
break;
|
|
2550
|
+
case 1:
|
|
2551
|
+
out[0] = -s;
|
|
2552
|
+
out[1] = c;
|
|
2553
|
+
break;
|
|
2554
|
+
case 2:
|
|
2555
|
+
out[0] = -c;
|
|
2556
|
+
out[1] = -s;
|
|
2557
|
+
break;
|
|
2558
|
+
default:
|
|
2559
|
+
out[0] = s;
|
|
2560
|
+
out[1] = -c;
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
const MAX_NODES = 2e4;
|
|
2564
|
+
function cosh(x) {
|
|
2565
|
+
return (detExp(x) + detExp(-x)) / 2;
|
|
2566
|
+
}
|
|
2567
|
+
function acosh(y) {
|
|
2568
|
+
return detLog(y + Math.sqrt((y - 1) * (y + 1)));
|
|
2569
|
+
}
|
|
2570
|
+
function planHyperbolic(options) {
|
|
2571
|
+
const { n, averageDegree, exponent } = options;
|
|
2572
|
+
const temperature = options.temperature ?? 0;
|
|
2573
|
+
checkInt("n", n, 0, MAX_NODES);
|
|
2574
|
+
if (!(Number.isFinite(averageDegree) && averageDegree > 0)) {
|
|
2575
|
+
throw new RangeError(`averageDegree must be a finite number > 0, got ${String(averageDegree)}`);
|
|
2576
|
+
}
|
|
2577
|
+
if (!(Number.isFinite(exponent) && exponent > 2)) {
|
|
2578
|
+
throw new RangeError(`exponent must be a finite number > 2, got ${String(exponent)}`);
|
|
2579
|
+
}
|
|
2580
|
+
if (!(temperature >= 0 && temperature < 1)) {
|
|
2581
|
+
throw new RangeError(`temperature must be in [0, 1), got ${String(temperature)}`);
|
|
2582
|
+
}
|
|
2583
|
+
const seed = resolveSeed(options.seed);
|
|
2584
|
+
const alpha = (exponent - 1) / 2;
|
|
2585
|
+
const xi = alpha / (alpha - 0.5);
|
|
2586
|
+
let correction = 1;
|
|
2587
|
+
if (temperature > 0) {
|
|
2588
|
+
const cs2 = new Float64Array(2);
|
|
2589
|
+
detCosSinTurn(temperature / 2, cs2);
|
|
2590
|
+
correction = Math.PI * temperature / cs2[1];
|
|
2591
|
+
}
|
|
2592
|
+
const scale2 = 2 * xi * xi * n / (Math.PI * averageDegree) * correction;
|
|
2593
|
+
if (n >= 2 && !(scale2 > 1)) {
|
|
2594
|
+
throw new RangeError(`averageDegree ${averageDegree} is too large for n = ${n}: the disk radius would be <= 0`);
|
|
2595
|
+
}
|
|
2596
|
+
const R = n >= 2 ? 2 * detLog(scale2) : 0;
|
|
2597
|
+
const coshAlphaR = cosh(alpha * R);
|
|
2598
|
+
if (!Number.isFinite(coshAlphaR)) {
|
|
2599
|
+
throw new RangeError(`exponent ${exponent} is too large: cosh(alpha R) overflows`);
|
|
2600
|
+
}
|
|
2601
|
+
const cosT = new Float64Array(n);
|
|
2602
|
+
const sinT = new Float64Array(n);
|
|
2603
|
+
const coshr = new Float64Array(n);
|
|
2604
|
+
const sinhr = new Float64Array(n);
|
|
2605
|
+
const x = new Float64Array(n);
|
|
2606
|
+
const y = new Float64Array(n);
|
|
2607
|
+
const radius = new Float64Array(n);
|
|
2608
|
+
const cs = new Float64Array(2);
|
|
2609
|
+
const stream = new RandomStream(seed, "hyperbolic-points", 0);
|
|
2610
|
+
for (let i = 0; i < n; i++) {
|
|
2611
|
+
stream.reset(i);
|
|
2612
|
+
detCosSinTurn(stream.nextFloat(), cs);
|
|
2613
|
+
const r = acosh(1 + (coshAlphaR - 1) * stream.nextFloat()) / alpha;
|
|
2614
|
+
const er = detExp(r);
|
|
2615
|
+
cosT[i] = cs[0];
|
|
2616
|
+
sinT[i] = cs[1];
|
|
2617
|
+
coshr[i] = (er + 1 / er) / 2;
|
|
2618
|
+
sinhr[i] = (er - 1 / er) / 2;
|
|
2619
|
+
radius[i] = r;
|
|
2620
|
+
x[i] = r * cs[0];
|
|
2621
|
+
y[i] = r * cs[1];
|
|
2622
|
+
}
|
|
2623
|
+
return { n, seed, temperature, R, coshR: cosh(R), cosT, sinT, coshr, sinhr, x, y, radius };
|
|
2624
|
+
}
|
|
2625
|
+
function hyperbolicRows(plan, start, end, out) {
|
|
2626
|
+
const { n, R, coshR, temperature, cosT, sinT, coshr, sinhr } = plan;
|
|
2627
|
+
const stream = new RandomStream(plan.seed, "hyperbolic", 0);
|
|
2628
|
+
for (let u = start; u < end; u++) {
|
|
2629
|
+
stream.reset(u);
|
|
2630
|
+
for (let v = u + 1; v < n; v++) {
|
|
2631
|
+
const coshD = coshr[u] * coshr[v] - sinhr[u] * sinhr[v] * (cosT[u] * cosT[v] + sinT[u] * sinT[v]);
|
|
2632
|
+
if (temperature === 0) {
|
|
2633
|
+
if (coshD < coshR) {
|
|
2634
|
+
out.push(u, v);
|
|
2635
|
+
}
|
|
2636
|
+
continue;
|
|
2637
|
+
}
|
|
2638
|
+
const t = stream.nextFloat();
|
|
2639
|
+
if (acosh(Math.max(1, coshD)) < R + 2 * temperature * detLog((1 - t) / t)) {
|
|
2640
|
+
out.push(u, v);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
function hyperbolicGraph(options) {
|
|
2646
|
+
const plan = planHyperbolic(options);
|
|
2647
|
+
const out = new EdgeBuffer(Math.ceil(plan.n * options.averageDegree * 0.75) + 16);
|
|
2648
|
+
hyperbolicRows(plan, 0, plan.n, out);
|
|
2649
|
+
return applyWeights(toGraph(plan.n, out, false, { x: plan.x, y: plan.y, radius: plan.radius }), options);
|
|
2650
|
+
}
|
|
2651
|
+
function forwardOffsets(diagonals) {
|
|
2652
|
+
const offsets = [
|
|
2653
|
+
[1, 0, 0],
|
|
2654
|
+
[0, 1, 0],
|
|
2655
|
+
[0, 0, 1]
|
|
2656
|
+
];
|
|
2657
|
+
if (diagonals) {
|
|
2658
|
+
for (let dl = 0; dl <= 1; dl++) {
|
|
2659
|
+
for (let dr = -1; dr <= 1; dr++) {
|
|
2660
|
+
for (let dc = -1; dc <= 1; dc++) {
|
|
2661
|
+
const positive = dl > 0 || dl === 0 && (dr > 0 || dr === 0 && dc > 0);
|
|
2662
|
+
const axis = Math.abs(dc) + Math.abs(dr) + Math.abs(dl) === 1;
|
|
2663
|
+
if (positive && !axis) {
|
|
2664
|
+
offsets.push([dc, dr, dl]);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
return offsets;
|
|
2671
|
+
}
|
|
2672
|
+
function grid3dGraph(options) {
|
|
2673
|
+
const { rows, cols, layers } = options;
|
|
2674
|
+
checkInt("rows", rows, 1);
|
|
2675
|
+
checkInt("cols", cols, 1);
|
|
2676
|
+
checkInt("layers", layers, 1);
|
|
2677
|
+
const n = rows * cols * layers;
|
|
2678
|
+
checkInt("rows * cols * layers", n, 1);
|
|
2679
|
+
const periodic = options.periodic === true;
|
|
2680
|
+
const directed = options.directed === true;
|
|
2681
|
+
if (periodic && directed) {
|
|
2682
|
+
throw new RangeError("directed grids cannot be periodic: the wrap-around arcs would close cycles");
|
|
2683
|
+
}
|
|
2684
|
+
const offsets = forwardOffsets(options.diagonals === true);
|
|
2685
|
+
checkEdgeCount(n * offsets.length);
|
|
2686
|
+
let blocked = null;
|
|
2687
|
+
if (options.obstacles !== void 0) {
|
|
2688
|
+
const p = options.obstacles;
|
|
2689
|
+
checkProbability("obstacles", p);
|
|
2690
|
+
if (p >= 1) {
|
|
2691
|
+
throw new RangeError("obstacles must be below 1");
|
|
2692
|
+
}
|
|
2693
|
+
blocked = new Uint8Array(n);
|
|
2694
|
+
const stream = new RandomStream(resolveSeed(options.seed), "grid-obstacles", 0);
|
|
2695
|
+
for (let i = 0; i < n; i++) {
|
|
2696
|
+
stream.reset(i);
|
|
2697
|
+
blocked[i] = stream.nextFloat() < p ? 1 : 0;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
const sizes = [cols, rows, layers];
|
|
2701
|
+
const wraps = sizes.map((size) => periodic && size >= 3);
|
|
2702
|
+
const plane = rows * cols;
|
|
2703
|
+
const out = new EdgeBuffer(n * Math.min(offsets.length, 3));
|
|
2704
|
+
const at = [0, 0, 0];
|
|
2705
|
+
for (let l = 0; l < layers; l++) {
|
|
2706
|
+
for (let r = 0; r < rows; r++) {
|
|
2707
|
+
for (let c = 0; c < cols; c++) {
|
|
2708
|
+
const i = l * plane + r * cols + c;
|
|
2709
|
+
if (blocked?.[i] === 1) {
|
|
2710
|
+
continue;
|
|
2711
|
+
}
|
|
2712
|
+
const here = [c, r, l];
|
|
2713
|
+
for (const offset of offsets) {
|
|
2714
|
+
let inside = true;
|
|
2715
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
2716
|
+
let x = here[axis] + offset[axis];
|
|
2717
|
+
if (x < 0 || x >= sizes[axis]) {
|
|
2718
|
+
if (!wraps[axis]) {
|
|
2719
|
+
inside = false;
|
|
2720
|
+
break;
|
|
2721
|
+
}
|
|
2722
|
+
x = (x + sizes[axis]) % sizes[axis];
|
|
2723
|
+
}
|
|
2724
|
+
at[axis] = x;
|
|
2725
|
+
}
|
|
2726
|
+
if (!inside) {
|
|
2727
|
+
continue;
|
|
2728
|
+
}
|
|
2729
|
+
const j = at[2] * plane + at[1] * cols + at[0];
|
|
2730
|
+
if (blocked?.[j] === 1) {
|
|
2731
|
+
continue;
|
|
2732
|
+
}
|
|
2733
|
+
out.push(directed && j < i ? j : i, directed && j < i ? i : j);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
const columns = {};
|
|
2739
|
+
if (blocked !== null) {
|
|
2740
|
+
columns.blocked = blocked;
|
|
2741
|
+
}
|
|
2742
|
+
if (options.positions === true) {
|
|
2743
|
+
const x = new Float64Array(n);
|
|
2744
|
+
const y = new Float64Array(n);
|
|
2745
|
+
const z = layers > 1 ? new Float64Array(n) : null;
|
|
2746
|
+
for (let i = 0; i < n; i++) {
|
|
2747
|
+
x[i] = i % cols;
|
|
2748
|
+
y[i] = Math.floor(i / cols) % rows;
|
|
2749
|
+
if (z !== null) {
|
|
2750
|
+
z[i] = Math.floor(i / plane);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
columns.x = x;
|
|
2754
|
+
columns.y = y;
|
|
2755
|
+
if (z !== null) {
|
|
2756
|
+
columns.z = z;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
const graph = toGraph(n, out, directed, Object.keys(columns).length > 0 ? columns : void 0);
|
|
2760
|
+
return applyWeights(graph, options);
|
|
2761
|
+
}
|
|
2762
|
+
function gridGraph(options) {
|
|
2763
|
+
return grid3dGraph({ ...options, layers: 1 });
|
|
2764
|
+
}
|
|
2765
|
+
const HALF_SQRT3 = 0.8660254037844386;
|
|
2766
|
+
function planarSize(options) {
|
|
2767
|
+
checkInt("rows", options.rows, 1);
|
|
2768
|
+
checkInt("cols", options.cols, 1);
|
|
2769
|
+
const n = options.rows * options.cols;
|
|
2770
|
+
checkInt("rows * cols", n, 1);
|
|
2771
|
+
checkEdgeCount(3 * n);
|
|
2772
|
+
return n;
|
|
2773
|
+
}
|
|
2774
|
+
function triangularLatticeGraph(options) {
|
|
2775
|
+
const n = planarSize(options);
|
|
2776
|
+
const { rows, cols } = options;
|
|
2777
|
+
const out = new EdgeBuffer(3 * n);
|
|
2778
|
+
for (let r = 0; r < rows; r++) {
|
|
2779
|
+
for (let c = 0; c < cols; c++) {
|
|
2780
|
+
const i = r * cols + c;
|
|
2781
|
+
if (c + 1 < cols) {
|
|
2782
|
+
out.push(i, i + 1);
|
|
2783
|
+
}
|
|
2784
|
+
if (r + 1 < rows) {
|
|
2785
|
+
out.push(i, i + cols);
|
|
2786
|
+
if (c > 0) {
|
|
2787
|
+
out.push(i, i + cols - 1);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
let columns;
|
|
2793
|
+
if (options.positions === true) {
|
|
2794
|
+
const x = new Float64Array(n);
|
|
2795
|
+
const y = new Float64Array(n);
|
|
2796
|
+
for (let i = 0; i < n; i++) {
|
|
2797
|
+
const r = Math.floor(i / cols);
|
|
2798
|
+
x[i] = i % cols + r / 2;
|
|
2799
|
+
y[i] = r * HALF_SQRT3;
|
|
2800
|
+
}
|
|
2801
|
+
columns = { x, y };
|
|
2802
|
+
}
|
|
2803
|
+
return applyWeights(toGraph(n, out, false, columns), options);
|
|
2804
|
+
}
|
|
2805
|
+
function hexagonalLatticeGraph(options) {
|
|
2806
|
+
const n = planarSize(options);
|
|
2807
|
+
const { rows, cols } = options;
|
|
2808
|
+
const out = new EdgeBuffer(2 * n);
|
|
2809
|
+
for (let r = 0; r < rows; r++) {
|
|
2810
|
+
for (let c = 0; c < cols; c++) {
|
|
2811
|
+
const i = r * cols + c;
|
|
2812
|
+
if (c + 1 < cols) {
|
|
2813
|
+
out.push(i, i + 1);
|
|
2814
|
+
}
|
|
2815
|
+
if (r + 1 < rows && (r + c) % 2 === 0) {
|
|
2816
|
+
out.push(i, i + cols);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
let columns;
|
|
2821
|
+
if (options.positions === true) {
|
|
2822
|
+
const x = new Float64Array(n);
|
|
2823
|
+
const y = new Float64Array(n);
|
|
2824
|
+
for (let i = 0; i < n; i++) {
|
|
2825
|
+
const r = Math.floor(i / cols);
|
|
2826
|
+
const c = i % cols;
|
|
2827
|
+
x[i] = c * HALF_SQRT3;
|
|
2828
|
+
y[i] = 1.5 * r + ((r + c) % 2 === 0 ? 0.5 : 0);
|
|
2829
|
+
}
|
|
2830
|
+
columns = { x, y };
|
|
2831
|
+
}
|
|
2832
|
+
return applyWeights(toGraph(n, out, false, columns), options);
|
|
2833
|
+
}
|
|
2834
|
+
const MAX_LFR_NODES = 1e6;
|
|
2835
|
+
function communitySizes(n, min, max, sample, seed) {
|
|
2836
|
+
if (Math.ceil(n / max) * min > n) {
|
|
2837
|
+
throw new RangeError(`there is no partition of ${n} nodes into communities of ${min} to ${max} nodes`);
|
|
2838
|
+
}
|
|
2839
|
+
const stream = new RandomStream(seed, "lfr-sizes", 0);
|
|
2840
|
+
const sizes = [];
|
|
2841
|
+
let excess = -n;
|
|
2842
|
+
while (excess < 0) {
|
|
2843
|
+
const s = sample(stream);
|
|
2844
|
+
sizes.push(s);
|
|
2845
|
+
excess += s;
|
|
2846
|
+
}
|
|
2847
|
+
if (sizes.length * min > n) {
|
|
2848
|
+
excess -= sizes[sizes.length - 1];
|
|
2849
|
+
sizes.pop();
|
|
2850
|
+
}
|
|
2851
|
+
for (let i = sizes.length - 1; i >= 0 && excess !== 0; i--) {
|
|
2852
|
+
const change = excess > 0 ? -Math.min(excess, sizes[i] - min) : Math.min(-excess, max - sizes[i]);
|
|
2853
|
+
sizes[i] += change;
|
|
2854
|
+
excess += change;
|
|
2855
|
+
}
|
|
2856
|
+
return sizes;
|
|
2857
|
+
}
|
|
2858
|
+
function assignCommunities(internal, sizes, seed) {
|
|
2859
|
+
const n = internal.length;
|
|
2860
|
+
const nodes = Uint32Array.from({ length: n }, (_, i) => i).sort((a, b) => internal[b] - internal[a] || a - b);
|
|
2861
|
+
const bySize = Uint32Array.from(sizes, (_, i) => i).sort((a, b) => sizes[b] - sizes[a] || a - b);
|
|
2862
|
+
const room = sizes.slice();
|
|
2863
|
+
const open = [];
|
|
2864
|
+
const community = new Uint32Array(n);
|
|
2865
|
+
const stream = new RandomStream(seed, "lfr-assign", 0);
|
|
2866
|
+
let next = 0;
|
|
2867
|
+
for (const node of nodes) {
|
|
2868
|
+
while (next < bySize.length && sizes[bySize[next]] > internal[node]) {
|
|
2869
|
+
open.push(bySize[next++]);
|
|
2870
|
+
}
|
|
2871
|
+
if (open.length === 0) {
|
|
2872
|
+
throw new RangeError(
|
|
2873
|
+
`could not assign communities: node ${node} needs a community of more than ${internal[node]} nodes; raise maxCommunity or mixing, or lower maxDegree`
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
const j = stream.nextBelow(open.length);
|
|
2877
|
+
const c = open[j];
|
|
2878
|
+
community[node] = c;
|
|
2879
|
+
if (--room[c] === 0) {
|
|
2880
|
+
open[j] = open[open.length - 1];
|
|
2881
|
+
open.pop();
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
return community;
|
|
2885
|
+
}
|
|
2886
|
+
function lfrGraph(options) {
|
|
2887
|
+
const { n, minDegree, maxDegree, minCommunity, maxCommunity, mixing } = options;
|
|
2888
|
+
const seed = resolveSeed(options.seed);
|
|
2889
|
+
checkInt("n", n, 1, MAX_LFR_NODES);
|
|
2890
|
+
checkInt("maxDegree", maxDegree, 1, n - 1);
|
|
2891
|
+
checkProbability("mixing", mixing);
|
|
2892
|
+
checkInt("maxCommunity", maxCommunity, 1, n);
|
|
2893
|
+
const degreeOf = powerLawSampler(
|
|
2894
|
+
["degreeExponent", "minDegree", "maxDegree"],
|
|
2895
|
+
options.degreeExponent,
|
|
2896
|
+
minDegree,
|
|
2897
|
+
maxDegree
|
|
2898
|
+
);
|
|
2899
|
+
const sizeOf = powerLawSampler(
|
|
2900
|
+
["communityExponent", "minCommunity", "maxCommunity"],
|
|
2901
|
+
options.communityExponent,
|
|
2902
|
+
minCommunity,
|
|
2903
|
+
maxCommunity
|
|
2904
|
+
);
|
|
2905
|
+
const degree = new Uint32Array(n);
|
|
2906
|
+
const degreeStream = new RandomStream(seed, "lfr-degrees", 0);
|
|
2907
|
+
for (let i = 0; i < n; i++) {
|
|
2908
|
+
degree[i] = degreeOf(degreeStream);
|
|
2909
|
+
}
|
|
2910
|
+
makeSumEven(degree, minDegree, maxDegree);
|
|
2911
|
+
const sizes = communitySizes(n, minCommunity, maxCommunity, sizeOf, seed);
|
|
2912
|
+
const internal = Uint32Array.from(degree, (d) => Math.round((1 - mixing) * d));
|
|
2913
|
+
const community = assignCommunities(internal, sizes, seed);
|
|
2914
|
+
const start = new Uint32Array(sizes.length + 1);
|
|
2915
|
+
for (let c = 0; c < sizes.length; c++) {
|
|
2916
|
+
start[c + 1] = start[c] + sizes[c];
|
|
2917
|
+
}
|
|
2918
|
+
const fill = start.slice(0, sizes.length);
|
|
2919
|
+
const members = new Uint32Array(n);
|
|
2920
|
+
for (let i = 0; i < n; i++) {
|
|
2921
|
+
members[fill[community[i]]++] = i;
|
|
2922
|
+
}
|
|
2923
|
+
let total = 0;
|
|
2924
|
+
for (const d of degree) {
|
|
2925
|
+
total += d;
|
|
2926
|
+
}
|
|
2927
|
+
const out = new EdgeBuffer(total / 2);
|
|
2928
|
+
const stream = new RandomStream(seed, "lfr-internal", 0);
|
|
2929
|
+
for (let c = 0; c < sizes.length; c++) {
|
|
2930
|
+
const group = members.subarray(start[c], start[c + 1]);
|
|
2931
|
+
const k = Uint32Array.from(group, (i) => internal[i]);
|
|
2932
|
+
let sum = 0;
|
|
2933
|
+
let largest = 0;
|
|
2934
|
+
for (let j = 0; j < k.length; j++) {
|
|
2935
|
+
sum += k[j];
|
|
2936
|
+
if (k[j] > k[largest]) {
|
|
2937
|
+
largest = j;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
if (sum % 2 === 1) {
|
|
2941
|
+
k[largest]--;
|
|
2942
|
+
internal[group[largest]]--;
|
|
2943
|
+
sum--;
|
|
2944
|
+
}
|
|
2945
|
+
const stubs2 = stubList(k, sum);
|
|
2946
|
+
for (let j = 0; j < stubs2.length; j++) {
|
|
2947
|
+
stubs2[j] = group[stubs2[j]];
|
|
2948
|
+
}
|
|
2949
|
+
stream.reset(c);
|
|
2950
|
+
shuffle(stubs2, stream);
|
|
2951
|
+
pairStubs(n, stubs2, false, false, out);
|
|
2952
|
+
}
|
|
2953
|
+
const external = Uint32Array.from(degree, (d, i) => d - internal[i]);
|
|
2954
|
+
let externalTotal = 0;
|
|
2955
|
+
for (const d of external) {
|
|
2956
|
+
externalTotal += d;
|
|
2957
|
+
}
|
|
2958
|
+
const stubs = stubList(external, externalTotal);
|
|
2959
|
+
shuffle(stubs, new RandomStream(seed, "lfr-external", 0));
|
|
2960
|
+
pairStubs(n, stubs, false, false, out, community);
|
|
2961
|
+
return applyWeights(toGraph(n, out, false, { community }), options);
|
|
2962
|
+
}
|
|
2963
|
+
const NAMED_GRAPH_NAMES = [
|
|
2964
|
+
"bull",
|
|
2965
|
+
"chvatal",
|
|
2966
|
+
"cubical",
|
|
2967
|
+
"desargues",
|
|
2968
|
+
"diamond",
|
|
2969
|
+
"dodecahedral",
|
|
2970
|
+
"frucht",
|
|
2971
|
+
"heawood",
|
|
2972
|
+
"hoffman-singleton",
|
|
2973
|
+
"house",
|
|
2974
|
+
"house-x",
|
|
2975
|
+
"icosahedral",
|
|
2976
|
+
"krackhardt-kite",
|
|
2977
|
+
"moebius-kantor",
|
|
2978
|
+
"octahedral",
|
|
2979
|
+
"pappus",
|
|
2980
|
+
"sedgewick-maze",
|
|
2981
|
+
"tetrahedral",
|
|
2982
|
+
"truncated-cube",
|
|
2983
|
+
"truncated-tetrahedron",
|
|
2984
|
+
"tutte"
|
|
2985
|
+
];
|
|
2986
|
+
const GRAPHS = {
|
|
2987
|
+
// a triangle with two pendant horns; 5 nodes, 5 edges
|
|
2988
|
+
bull: [5, [0, 1, 0, 2, 1, 2, 1, 3, 2, 4]],
|
|
2989
|
+
// the smallest triangle-free 4-regular 4-chromatic graph; 12 nodes, 24 edges
|
|
2990
|
+
chvatal: [
|
|
2991
|
+
12,
|
|
2992
|
+
[
|
|
2993
|
+
0,
|
|
2994
|
+
1,
|
|
2995
|
+
0,
|
|
2996
|
+
4,
|
|
2997
|
+
0,
|
|
2998
|
+
6,
|
|
2999
|
+
0,
|
|
3000
|
+
9,
|
|
3001
|
+
1,
|
|
3002
|
+
2,
|
|
3003
|
+
1,
|
|
3004
|
+
5,
|
|
3005
|
+
1,
|
|
3006
|
+
7,
|
|
3007
|
+
2,
|
|
3008
|
+
3,
|
|
3009
|
+
2,
|
|
3010
|
+
6,
|
|
3011
|
+
2,
|
|
3012
|
+
8,
|
|
3013
|
+
3,
|
|
3014
|
+
4,
|
|
3015
|
+
3,
|
|
3016
|
+
7,
|
|
3017
|
+
3,
|
|
3018
|
+
9,
|
|
3019
|
+
4,
|
|
3020
|
+
5,
|
|
3021
|
+
4,
|
|
3022
|
+
8,
|
|
3023
|
+
5,
|
|
3024
|
+
10,
|
|
3025
|
+
5,
|
|
3026
|
+
11,
|
|
3027
|
+
6,
|
|
3028
|
+
10,
|
|
3029
|
+
6,
|
|
3030
|
+
11,
|
|
3031
|
+
7,
|
|
3032
|
+
8,
|
|
3033
|
+
7,
|
|
3034
|
+
11,
|
|
3035
|
+
8,
|
|
3036
|
+
10,
|
|
3037
|
+
9,
|
|
3038
|
+
10,
|
|
3039
|
+
9,
|
|
3040
|
+
11
|
|
3041
|
+
]
|
|
3042
|
+
],
|
|
3043
|
+
// the cube's skeleton (Q_3); 8 nodes, 12 edges, planar
|
|
3044
|
+
cubical: [8, [0, 1, 0, 3, 0, 4, 1, 2, 1, 7, 2, 3, 2, 6, 3, 5, 4, 5, 4, 7, 5, 6, 6, 7]],
|
|
3045
|
+
// the Desargues graph, 3-regular, distance-transitive; 20 nodes, 30 edges
|
|
3046
|
+
desargues: [
|
|
3047
|
+
20,
|
|
3048
|
+
[
|
|
3049
|
+
0,
|
|
3050
|
+
1,
|
|
3051
|
+
0,
|
|
3052
|
+
19,
|
|
3053
|
+
0,
|
|
3054
|
+
5,
|
|
3055
|
+
1,
|
|
3056
|
+
2,
|
|
3057
|
+
1,
|
|
3058
|
+
16,
|
|
3059
|
+
2,
|
|
3060
|
+
3,
|
|
3061
|
+
2,
|
|
3062
|
+
11,
|
|
3063
|
+
3,
|
|
3064
|
+
4,
|
|
3065
|
+
3,
|
|
3066
|
+
14,
|
|
3067
|
+
4,
|
|
3068
|
+
5,
|
|
3069
|
+
4,
|
|
3070
|
+
9,
|
|
3071
|
+
5,
|
|
3072
|
+
6,
|
|
3073
|
+
6,
|
|
3074
|
+
7,
|
|
3075
|
+
6,
|
|
3076
|
+
15,
|
|
3077
|
+
7,
|
|
3078
|
+
8,
|
|
3079
|
+
7,
|
|
3080
|
+
18,
|
|
3081
|
+
8,
|
|
3082
|
+
9,
|
|
3083
|
+
8,
|
|
3084
|
+
13,
|
|
3085
|
+
9,
|
|
3086
|
+
10,
|
|
3087
|
+
10,
|
|
3088
|
+
11,
|
|
3089
|
+
10,
|
|
3090
|
+
19,
|
|
3091
|
+
11,
|
|
3092
|
+
12,
|
|
3093
|
+
12,
|
|
3094
|
+
13,
|
|
3095
|
+
12,
|
|
3096
|
+
17,
|
|
3097
|
+
13,
|
|
3098
|
+
14,
|
|
3099
|
+
14,
|
|
3100
|
+
15,
|
|
3101
|
+
15,
|
|
3102
|
+
16,
|
|
3103
|
+
16,
|
|
3104
|
+
17,
|
|
3105
|
+
17,
|
|
3106
|
+
18,
|
|
3107
|
+
18,
|
|
3108
|
+
19
|
|
3109
|
+
]
|
|
3110
|
+
],
|
|
3111
|
+
// K_4 minus one edge; 4 nodes, 5 edges
|
|
3112
|
+
diamond: [4, [0, 1, 0, 2, 1, 2, 1, 3, 2, 3]],
|
|
3113
|
+
// the dodecahedron's skeleton; 20 nodes, 30 edges, planar, 3-regular
|
|
3114
|
+
dodecahedral: [
|
|
3115
|
+
20,
|
|
3116
|
+
[
|
|
3117
|
+
0,
|
|
3118
|
+
1,
|
|
3119
|
+
0,
|
|
3120
|
+
19,
|
|
3121
|
+
0,
|
|
3122
|
+
10,
|
|
3123
|
+
1,
|
|
3124
|
+
2,
|
|
3125
|
+
1,
|
|
3126
|
+
8,
|
|
3127
|
+
2,
|
|
3128
|
+
3,
|
|
3129
|
+
2,
|
|
3130
|
+
6,
|
|
3131
|
+
3,
|
|
3132
|
+
4,
|
|
3133
|
+
3,
|
|
3134
|
+
19,
|
|
3135
|
+
4,
|
|
3136
|
+
5,
|
|
3137
|
+
4,
|
|
3138
|
+
17,
|
|
3139
|
+
5,
|
|
3140
|
+
6,
|
|
3141
|
+
5,
|
|
3142
|
+
15,
|
|
3143
|
+
6,
|
|
3144
|
+
7,
|
|
3145
|
+
7,
|
|
3146
|
+
8,
|
|
3147
|
+
7,
|
|
3148
|
+
14,
|
|
3149
|
+
8,
|
|
3150
|
+
9,
|
|
3151
|
+
9,
|
|
3152
|
+
10,
|
|
3153
|
+
9,
|
|
3154
|
+
13,
|
|
3155
|
+
10,
|
|
3156
|
+
11,
|
|
3157
|
+
11,
|
|
3158
|
+
12,
|
|
3159
|
+
11,
|
|
3160
|
+
18,
|
|
3161
|
+
12,
|
|
3162
|
+
13,
|
|
3163
|
+
12,
|
|
3164
|
+
16,
|
|
3165
|
+
13,
|
|
3166
|
+
14,
|
|
3167
|
+
14,
|
|
3168
|
+
15,
|
|
3169
|
+
15,
|
|
3170
|
+
16,
|
|
3171
|
+
16,
|
|
3172
|
+
17,
|
|
3173
|
+
17,
|
|
3174
|
+
18,
|
|
3175
|
+
18,
|
|
3176
|
+
19
|
|
3177
|
+
]
|
|
3178
|
+
],
|
|
3179
|
+
// 3-regular with no non-trivial automorphism; 12 nodes, 18 edges
|
|
3180
|
+
frucht: [
|
|
3181
|
+
12,
|
|
3182
|
+
[
|
|
3183
|
+
0,
|
|
3184
|
+
1,
|
|
3185
|
+
0,
|
|
3186
|
+
6,
|
|
3187
|
+
0,
|
|
3188
|
+
7,
|
|
3189
|
+
1,
|
|
3190
|
+
2,
|
|
3191
|
+
1,
|
|
3192
|
+
7,
|
|
3193
|
+
2,
|
|
3194
|
+
3,
|
|
3195
|
+
2,
|
|
3196
|
+
8,
|
|
3197
|
+
3,
|
|
3198
|
+
4,
|
|
3199
|
+
3,
|
|
3200
|
+
9,
|
|
3201
|
+
4,
|
|
3202
|
+
5,
|
|
3203
|
+
4,
|
|
3204
|
+
9,
|
|
3205
|
+
5,
|
|
3206
|
+
6,
|
|
3207
|
+
5,
|
|
3208
|
+
10,
|
|
3209
|
+
6,
|
|
3210
|
+
10,
|
|
3211
|
+
7,
|
|
3212
|
+
11,
|
|
3213
|
+
8,
|
|
3214
|
+
11,
|
|
3215
|
+
8,
|
|
3216
|
+
9,
|
|
3217
|
+
10,
|
|
3218
|
+
11
|
|
3219
|
+
]
|
|
3220
|
+
],
|
|
3221
|
+
// the (3,6)-cage, the Fano plane's incidence graph; 14 nodes, 21 edges
|
|
3222
|
+
heawood: [
|
|
3223
|
+
14,
|
|
3224
|
+
[
|
|
3225
|
+
0,
|
|
3226
|
+
1,
|
|
3227
|
+
0,
|
|
3228
|
+
13,
|
|
3229
|
+
0,
|
|
3230
|
+
5,
|
|
3231
|
+
1,
|
|
3232
|
+
2,
|
|
3233
|
+
1,
|
|
3234
|
+
10,
|
|
3235
|
+
2,
|
|
3236
|
+
3,
|
|
3237
|
+
2,
|
|
3238
|
+
7,
|
|
3239
|
+
3,
|
|
3240
|
+
4,
|
|
3241
|
+
3,
|
|
3242
|
+
12,
|
|
3243
|
+
4,
|
|
3244
|
+
5,
|
|
3245
|
+
4,
|
|
3246
|
+
9,
|
|
3247
|
+
5,
|
|
3248
|
+
6,
|
|
3249
|
+
6,
|
|
3250
|
+
7,
|
|
3251
|
+
6,
|
|
3252
|
+
11,
|
|
3253
|
+
7,
|
|
3254
|
+
8,
|
|
3255
|
+
8,
|
|
3256
|
+
9,
|
|
3257
|
+
8,
|
|
3258
|
+
13,
|
|
3259
|
+
9,
|
|
3260
|
+
10,
|
|
3261
|
+
10,
|
|
3262
|
+
11,
|
|
3263
|
+
11,
|
|
3264
|
+
12,
|
|
3265
|
+
12,
|
|
3266
|
+
13
|
|
3267
|
+
]
|
|
3268
|
+
],
|
|
3269
|
+
// the (7,5)-cage, a Moore graph; 50 nodes, 175 edges
|
|
3270
|
+
"hoffman-singleton": [
|
|
3271
|
+
50,
|
|
3272
|
+
[
|
|
3273
|
+
0,
|
|
3274
|
+
1,
|
|
3275
|
+
0,
|
|
3276
|
+
2,
|
|
3277
|
+
0,
|
|
3278
|
+
3,
|
|
3279
|
+
0,
|
|
3280
|
+
6,
|
|
3281
|
+
0,
|
|
3282
|
+
7,
|
|
3283
|
+
0,
|
|
3284
|
+
8,
|
|
3285
|
+
0,
|
|
3286
|
+
9,
|
|
3287
|
+
1,
|
|
3288
|
+
17,
|
|
3289
|
+
1,
|
|
3290
|
+
12,
|
|
3291
|
+
1,
|
|
3292
|
+
26,
|
|
3293
|
+
1,
|
|
3294
|
+
27,
|
|
3295
|
+
1,
|
|
3296
|
+
28,
|
|
3297
|
+
1,
|
|
3298
|
+
29,
|
|
3299
|
+
2,
|
|
3300
|
+
10,
|
|
3301
|
+
2,
|
|
3302
|
+
11,
|
|
3303
|
+
2,
|
|
3304
|
+
13,
|
|
3305
|
+
2,
|
|
3306
|
+
14,
|
|
3307
|
+
2,
|
|
3308
|
+
15,
|
|
3309
|
+
2,
|
|
3310
|
+
16,
|
|
3311
|
+
3,
|
|
3312
|
+
4,
|
|
3313
|
+
3,
|
|
3314
|
+
5,
|
|
3315
|
+
3,
|
|
3316
|
+
30,
|
|
3317
|
+
3,
|
|
3318
|
+
35,
|
|
3319
|
+
3,
|
|
3320
|
+
40,
|
|
3321
|
+
3,
|
|
3322
|
+
45,
|
|
3323
|
+
4,
|
|
3324
|
+
11,
|
|
3325
|
+
4,
|
|
3326
|
+
17,
|
|
3327
|
+
4,
|
|
3328
|
+
34,
|
|
3329
|
+
4,
|
|
3330
|
+
39,
|
|
3331
|
+
4,
|
|
3332
|
+
44,
|
|
3333
|
+
4,
|
|
3334
|
+
49,
|
|
3335
|
+
5,
|
|
3336
|
+
12,
|
|
3337
|
+
5,
|
|
3338
|
+
10,
|
|
3339
|
+
5,
|
|
3340
|
+
33,
|
|
3341
|
+
5,
|
|
3342
|
+
38,
|
|
3343
|
+
5,
|
|
3344
|
+
43,
|
|
3345
|
+
5,
|
|
3346
|
+
48,
|
|
3347
|
+
6,
|
|
3348
|
+
22,
|
|
3349
|
+
6,
|
|
3350
|
+
18,
|
|
3351
|
+
6,
|
|
3352
|
+
31,
|
|
3353
|
+
6,
|
|
3354
|
+
39,
|
|
3355
|
+
6,
|
|
3356
|
+
43,
|
|
3357
|
+
6,
|
|
3358
|
+
47,
|
|
3359
|
+
7,
|
|
3360
|
+
34,
|
|
3361
|
+
7,
|
|
3362
|
+
23,
|
|
3363
|
+
7,
|
|
3364
|
+
19,
|
|
3365
|
+
7,
|
|
3366
|
+
37,
|
|
3367
|
+
7,
|
|
3368
|
+
41,
|
|
3369
|
+
7,
|
|
3370
|
+
48,
|
|
3371
|
+
8,
|
|
3372
|
+
33,
|
|
3373
|
+
8,
|
|
3374
|
+
36,
|
|
3375
|
+
8,
|
|
3376
|
+
24,
|
|
3377
|
+
8,
|
|
3378
|
+
20,
|
|
3379
|
+
8,
|
|
3380
|
+
42,
|
|
3381
|
+
8,
|
|
3382
|
+
49,
|
|
3383
|
+
9,
|
|
3384
|
+
32,
|
|
3385
|
+
9,
|
|
3386
|
+
38,
|
|
3387
|
+
9,
|
|
3388
|
+
44,
|
|
3389
|
+
9,
|
|
3390
|
+
25,
|
|
3391
|
+
9,
|
|
3392
|
+
21,
|
|
3393
|
+
9,
|
|
3394
|
+
46,
|
|
3395
|
+
10,
|
|
3396
|
+
17,
|
|
3397
|
+
10,
|
|
3398
|
+
18,
|
|
3399
|
+
10,
|
|
3400
|
+
19,
|
|
3401
|
+
10,
|
|
3402
|
+
20,
|
|
3403
|
+
10,
|
|
3404
|
+
21,
|
|
3405
|
+
11,
|
|
3406
|
+
12,
|
|
3407
|
+
11,
|
|
3408
|
+
32,
|
|
3409
|
+
11,
|
|
3410
|
+
37,
|
|
3411
|
+
11,
|
|
3412
|
+
42,
|
|
3413
|
+
11,
|
|
3414
|
+
47,
|
|
3415
|
+
12,
|
|
3416
|
+
31,
|
|
3417
|
+
12,
|
|
3418
|
+
36,
|
|
3419
|
+
12,
|
|
3420
|
+
41,
|
|
3421
|
+
12,
|
|
3422
|
+
46,
|
|
3423
|
+
13,
|
|
3424
|
+
30,
|
|
3425
|
+
13,
|
|
3426
|
+
26,
|
|
3427
|
+
13,
|
|
3428
|
+
22,
|
|
3429
|
+
13,
|
|
3430
|
+
36,
|
|
3431
|
+
13,
|
|
3432
|
+
44,
|
|
3433
|
+
13,
|
|
3434
|
+
48,
|
|
3435
|
+
14,
|
|
3436
|
+
31,
|
|
3437
|
+
14,
|
|
3438
|
+
27,
|
|
3439
|
+
14,
|
|
3440
|
+
23,
|
|
3441
|
+
14,
|
|
3442
|
+
38,
|
|
3443
|
+
14,
|
|
3444
|
+
40,
|
|
3445
|
+
14,
|
|
3446
|
+
49,
|
|
3447
|
+
15,
|
|
3448
|
+
34,
|
|
3449
|
+
15,
|
|
3450
|
+
35,
|
|
3451
|
+
15,
|
|
3452
|
+
28,
|
|
3453
|
+
15,
|
|
3454
|
+
24,
|
|
3455
|
+
15,
|
|
3456
|
+
43,
|
|
3457
|
+
15,
|
|
3458
|
+
46,
|
|
3459
|
+
16,
|
|
3460
|
+
33,
|
|
3461
|
+
16,
|
|
3462
|
+
39,
|
|
3463
|
+
16,
|
|
3464
|
+
41,
|
|
3465
|
+
16,
|
|
3466
|
+
45,
|
|
3467
|
+
16,
|
|
3468
|
+
29,
|
|
3469
|
+
16,
|
|
3470
|
+
25,
|
|
3471
|
+
17,
|
|
3472
|
+
22,
|
|
3473
|
+
17,
|
|
3474
|
+
23,
|
|
3475
|
+
17,
|
|
3476
|
+
24,
|
|
3477
|
+
17,
|
|
3478
|
+
25,
|
|
3479
|
+
18,
|
|
3480
|
+
32,
|
|
3481
|
+
18,
|
|
3482
|
+
26,
|
|
3483
|
+
18,
|
|
3484
|
+
35,
|
|
3485
|
+
18,
|
|
3486
|
+
41,
|
|
3487
|
+
18,
|
|
3488
|
+
49,
|
|
3489
|
+
19,
|
|
3490
|
+
30,
|
|
3491
|
+
19,
|
|
3492
|
+
27,
|
|
3493
|
+
19,
|
|
3494
|
+
39,
|
|
3495
|
+
19,
|
|
3496
|
+
42,
|
|
3497
|
+
19,
|
|
3498
|
+
46,
|
|
3499
|
+
20,
|
|
3500
|
+
31,
|
|
3501
|
+
20,
|
|
3502
|
+
37,
|
|
3503
|
+
20,
|
|
3504
|
+
28,
|
|
3505
|
+
20,
|
|
3506
|
+
44,
|
|
3507
|
+
20,
|
|
3508
|
+
45,
|
|
3509
|
+
21,
|
|
3510
|
+
34,
|
|
3511
|
+
21,
|
|
3512
|
+
36,
|
|
3513
|
+
21,
|
|
3514
|
+
40,
|
|
3515
|
+
21,
|
|
3516
|
+
47,
|
|
3517
|
+
21,
|
|
3518
|
+
29,
|
|
3519
|
+
22,
|
|
3520
|
+
33,
|
|
3521
|
+
22,
|
|
3522
|
+
37,
|
|
3523
|
+
22,
|
|
3524
|
+
40,
|
|
3525
|
+
22,
|
|
3526
|
+
46,
|
|
3527
|
+
23,
|
|
3528
|
+
32,
|
|
3529
|
+
23,
|
|
3530
|
+
36,
|
|
3531
|
+
23,
|
|
3532
|
+
43,
|
|
3533
|
+
23,
|
|
3534
|
+
45,
|
|
3535
|
+
24,
|
|
3536
|
+
30,
|
|
3537
|
+
24,
|
|
3538
|
+
38,
|
|
3539
|
+
24,
|
|
3540
|
+
41,
|
|
3541
|
+
24,
|
|
3542
|
+
47,
|
|
3543
|
+
25,
|
|
3544
|
+
31,
|
|
3545
|
+
25,
|
|
3546
|
+
35,
|
|
3547
|
+
25,
|
|
3548
|
+
42,
|
|
3549
|
+
25,
|
|
3550
|
+
48,
|
|
3551
|
+
26,
|
|
3552
|
+
34,
|
|
3553
|
+
26,
|
|
3554
|
+
38,
|
|
3555
|
+
26,
|
|
3556
|
+
42,
|
|
3557
|
+
26,
|
|
3558
|
+
45,
|
|
3559
|
+
27,
|
|
3560
|
+
33,
|
|
3561
|
+
27,
|
|
3562
|
+
35,
|
|
3563
|
+
27,
|
|
3564
|
+
44,
|
|
3565
|
+
27,
|
|
3566
|
+
47,
|
|
3567
|
+
28,
|
|
3568
|
+
32,
|
|
3569
|
+
28,
|
|
3570
|
+
39,
|
|
3571
|
+
28,
|
|
3572
|
+
40,
|
|
3573
|
+
28,
|
|
3574
|
+
48,
|
|
3575
|
+
29,
|
|
3576
|
+
30,
|
|
3577
|
+
29,
|
|
3578
|
+
37,
|
|
3579
|
+
29,
|
|
3580
|
+
43,
|
|
3581
|
+
29,
|
|
3582
|
+
49,
|
|
3583
|
+
30,
|
|
3584
|
+
31,
|
|
3585
|
+
30,
|
|
3586
|
+
32,
|
|
3587
|
+
31,
|
|
3588
|
+
34,
|
|
3589
|
+
32,
|
|
3590
|
+
33,
|
|
3591
|
+
33,
|
|
3592
|
+
34,
|
|
3593
|
+
35,
|
|
3594
|
+
36,
|
|
3595
|
+
35,
|
|
3596
|
+
37,
|
|
3597
|
+
36,
|
|
3598
|
+
39,
|
|
3599
|
+
37,
|
|
3600
|
+
38,
|
|
3601
|
+
38,
|
|
3602
|
+
39,
|
|
3603
|
+
40,
|
|
3604
|
+
41,
|
|
3605
|
+
40,
|
|
3606
|
+
42,
|
|
3607
|
+
41,
|
|
3608
|
+
44,
|
|
3609
|
+
42,
|
|
3610
|
+
43,
|
|
3611
|
+
43,
|
|
3612
|
+
44,
|
|
3613
|
+
45,
|
|
3614
|
+
46,
|
|
3615
|
+
45,
|
|
3616
|
+
47,
|
|
3617
|
+
46,
|
|
3618
|
+
49,
|
|
3619
|
+
47,
|
|
3620
|
+
48,
|
|
3621
|
+
48,
|
|
3622
|
+
49
|
|
3623
|
+
]
|
|
3624
|
+
],
|
|
3625
|
+
// a square with a triangular roof; 5 nodes, 6 edges
|
|
3626
|
+
house: [5, [0, 1, 0, 2, 1, 3, 2, 3, 2, 4, 3, 4]],
|
|
3627
|
+
// the house with both diagonals of the square; 5 nodes, 8 edges
|
|
3628
|
+
"house-x": [5, [0, 1, 0, 2, 0, 3, 1, 3, 1, 2, 2, 3, 2, 4, 3, 4]],
|
|
3629
|
+
// the icosahedron's skeleton; 12 nodes, 30 edges, planar, 5-regular
|
|
3630
|
+
icosahedral: [
|
|
3631
|
+
12,
|
|
3632
|
+
[
|
|
3633
|
+
0,
|
|
3634
|
+
1,
|
|
3635
|
+
0,
|
|
3636
|
+
5,
|
|
3637
|
+
0,
|
|
3638
|
+
7,
|
|
3639
|
+
0,
|
|
3640
|
+
8,
|
|
3641
|
+
0,
|
|
3642
|
+
11,
|
|
3643
|
+
1,
|
|
3644
|
+
2,
|
|
3645
|
+
1,
|
|
3646
|
+
5,
|
|
3647
|
+
1,
|
|
3648
|
+
6,
|
|
3649
|
+
1,
|
|
3650
|
+
8,
|
|
3651
|
+
2,
|
|
3652
|
+
3,
|
|
3653
|
+
2,
|
|
3654
|
+
6,
|
|
3655
|
+
2,
|
|
3656
|
+
8,
|
|
3657
|
+
2,
|
|
3658
|
+
9,
|
|
3659
|
+
3,
|
|
3660
|
+
4,
|
|
3661
|
+
3,
|
|
3662
|
+
6,
|
|
3663
|
+
3,
|
|
3664
|
+
9,
|
|
3665
|
+
3,
|
|
3666
|
+
10,
|
|
3667
|
+
4,
|
|
3668
|
+
5,
|
|
3669
|
+
4,
|
|
3670
|
+
6,
|
|
3671
|
+
4,
|
|
3672
|
+
10,
|
|
3673
|
+
4,
|
|
3674
|
+
11,
|
|
3675
|
+
5,
|
|
3676
|
+
6,
|
|
3677
|
+
5,
|
|
3678
|
+
11,
|
|
3679
|
+
7,
|
|
3680
|
+
8,
|
|
3681
|
+
7,
|
|
3682
|
+
9,
|
|
3683
|
+
7,
|
|
3684
|
+
10,
|
|
3685
|
+
7,
|
|
3686
|
+
11,
|
|
3687
|
+
8,
|
|
3688
|
+
9,
|
|
3689
|
+
9,
|
|
3690
|
+
10,
|
|
3691
|
+
10,
|
|
3692
|
+
11
|
|
3693
|
+
]
|
|
3694
|
+
],
|
|
3695
|
+
// Krackhardt's kite: degree, betweenness and closeness each crown a different node; 10 nodes, 18 edges
|
|
3696
|
+
"krackhardt-kite": [
|
|
3697
|
+
10,
|
|
3698
|
+
[0, 1, 0, 2, 0, 3, 0, 5, 1, 3, 1, 4, 1, 6, 2, 3, 2, 5, 3, 4, 3, 5, 3, 6, 4, 6, 5, 6, 5, 7, 6, 7, 7, 8, 8, 9]
|
|
3699
|
+
],
|
|
3700
|
+
// the Moebius-Kantor graph, the generalized Petersen graph GP(8, 3); 16 nodes, 24 edges
|
|
3701
|
+
"moebius-kantor": [
|
|
3702
|
+
16,
|
|
3703
|
+
[
|
|
3704
|
+
0,
|
|
3705
|
+
1,
|
|
3706
|
+
0,
|
|
3707
|
+
15,
|
|
3708
|
+
0,
|
|
3709
|
+
5,
|
|
3710
|
+
1,
|
|
3711
|
+
2,
|
|
3712
|
+
1,
|
|
3713
|
+
12,
|
|
3714
|
+
2,
|
|
3715
|
+
3,
|
|
3716
|
+
2,
|
|
3717
|
+
7,
|
|
3718
|
+
3,
|
|
3719
|
+
4,
|
|
3720
|
+
3,
|
|
3721
|
+
14,
|
|
3722
|
+
4,
|
|
3723
|
+
5,
|
|
3724
|
+
4,
|
|
3725
|
+
9,
|
|
3726
|
+
5,
|
|
3727
|
+
6,
|
|
3728
|
+
6,
|
|
3729
|
+
7,
|
|
3730
|
+
6,
|
|
3731
|
+
11,
|
|
3732
|
+
7,
|
|
3733
|
+
8,
|
|
3734
|
+
8,
|
|
3735
|
+
9,
|
|
3736
|
+
8,
|
|
3737
|
+
13,
|
|
3738
|
+
9,
|
|
3739
|
+
10,
|
|
3740
|
+
10,
|
|
3741
|
+
11,
|
|
3742
|
+
10,
|
|
3743
|
+
15,
|
|
3744
|
+
11,
|
|
3745
|
+
12,
|
|
3746
|
+
12,
|
|
3747
|
+
13,
|
|
3748
|
+
13,
|
|
3749
|
+
14,
|
|
3750
|
+
14,
|
|
3751
|
+
15
|
|
3752
|
+
]
|
|
3753
|
+
],
|
|
3754
|
+
// the octahedron's skeleton, K_{2,2,2}; 6 nodes, 12 edges
|
|
3755
|
+
octahedral: [6, [0, 1, 0, 2, 0, 3, 0, 4, 1, 2, 1, 3, 1, 5, 2, 4, 2, 5, 3, 4, 3, 5, 4, 5]],
|
|
3756
|
+
// the Pappus graph, 3-regular, bipartite, distance-regular; 18 nodes, 27 edges
|
|
3757
|
+
pappus: [
|
|
3758
|
+
18,
|
|
3759
|
+
[
|
|
3760
|
+
0,
|
|
3761
|
+
1,
|
|
3762
|
+
0,
|
|
3763
|
+
17,
|
|
3764
|
+
0,
|
|
3765
|
+
5,
|
|
3766
|
+
1,
|
|
3767
|
+
2,
|
|
3768
|
+
1,
|
|
3769
|
+
8,
|
|
3770
|
+
2,
|
|
3771
|
+
3,
|
|
3772
|
+
2,
|
|
3773
|
+
13,
|
|
3774
|
+
3,
|
|
3775
|
+
4,
|
|
3776
|
+
3,
|
|
3777
|
+
10,
|
|
3778
|
+
4,
|
|
3779
|
+
5,
|
|
3780
|
+
4,
|
|
3781
|
+
15,
|
|
3782
|
+
5,
|
|
3783
|
+
6,
|
|
3784
|
+
6,
|
|
3785
|
+
7,
|
|
3786
|
+
6,
|
|
3787
|
+
11,
|
|
3788
|
+
7,
|
|
3789
|
+
8,
|
|
3790
|
+
7,
|
|
3791
|
+
14,
|
|
3792
|
+
8,
|
|
3793
|
+
9,
|
|
3794
|
+
9,
|
|
3795
|
+
10,
|
|
3796
|
+
9,
|
|
3797
|
+
16,
|
|
3798
|
+
10,
|
|
3799
|
+
11,
|
|
3800
|
+
11,
|
|
3801
|
+
12,
|
|
3802
|
+
12,
|
|
3803
|
+
13,
|
|
3804
|
+
12,
|
|
3805
|
+
17,
|
|
3806
|
+
13,
|
|
3807
|
+
14,
|
|
3808
|
+
14,
|
|
3809
|
+
15,
|
|
3810
|
+
15,
|
|
3811
|
+
16,
|
|
3812
|
+
16,
|
|
3813
|
+
17
|
|
3814
|
+
]
|
|
3815
|
+
],
|
|
3816
|
+
// the small maze of Sedgewick's Algorithms in C (DFS teaching); 8 nodes, 10 edges
|
|
3817
|
+
"sedgewick-maze": [8, [0, 2, 0, 7, 0, 5, 1, 7, 2, 6, 3, 4, 3, 5, 4, 5, 4, 7, 4, 6]],
|
|
3818
|
+
// the tetrahedron's skeleton, K_4; 4 nodes, 6 edges
|
|
3819
|
+
tetrahedral: [4, [0, 1, 0, 2, 0, 3, 1, 2, 1, 3, 2, 3]],
|
|
3820
|
+
// the truncated cube's skeleton; 24 nodes, 36 edges, planar
|
|
3821
|
+
"truncated-cube": [
|
|
3822
|
+
24,
|
|
3823
|
+
[
|
|
3824
|
+
0,
|
|
3825
|
+
1,
|
|
3826
|
+
0,
|
|
3827
|
+
2,
|
|
3828
|
+
0,
|
|
3829
|
+
4,
|
|
3830
|
+
1,
|
|
3831
|
+
11,
|
|
3832
|
+
1,
|
|
3833
|
+
14,
|
|
3834
|
+
2,
|
|
3835
|
+
3,
|
|
3836
|
+
2,
|
|
3837
|
+
4,
|
|
3838
|
+
3,
|
|
3839
|
+
6,
|
|
3840
|
+
3,
|
|
3841
|
+
8,
|
|
3842
|
+
4,
|
|
3843
|
+
5,
|
|
3844
|
+
5,
|
|
3845
|
+
16,
|
|
3846
|
+
5,
|
|
3847
|
+
18,
|
|
3848
|
+
6,
|
|
3849
|
+
7,
|
|
3850
|
+
6,
|
|
3851
|
+
8,
|
|
3852
|
+
7,
|
|
3853
|
+
10,
|
|
3854
|
+
7,
|
|
3855
|
+
12,
|
|
3856
|
+
8,
|
|
3857
|
+
9,
|
|
3858
|
+
9,
|
|
3859
|
+
17,
|
|
3860
|
+
9,
|
|
3861
|
+
20,
|
|
3862
|
+
10,
|
|
3863
|
+
11,
|
|
3864
|
+
10,
|
|
3865
|
+
12,
|
|
3866
|
+
11,
|
|
3867
|
+
14,
|
|
3868
|
+
12,
|
|
3869
|
+
13,
|
|
3870
|
+
13,
|
|
3871
|
+
21,
|
|
3872
|
+
13,
|
|
3873
|
+
22,
|
|
3874
|
+
14,
|
|
3875
|
+
15,
|
|
3876
|
+
15,
|
|
3877
|
+
19,
|
|
3878
|
+
15,
|
|
3879
|
+
23,
|
|
3880
|
+
16,
|
|
3881
|
+
17,
|
|
3882
|
+
16,
|
|
3883
|
+
18,
|
|
3884
|
+
17,
|
|
3885
|
+
20,
|
|
3886
|
+
18,
|
|
3887
|
+
19,
|
|
3888
|
+
19,
|
|
3889
|
+
23,
|
|
3890
|
+
20,
|
|
3891
|
+
21,
|
|
3892
|
+
21,
|
|
3893
|
+
22,
|
|
3894
|
+
22,
|
|
3895
|
+
23
|
|
3896
|
+
]
|
|
3897
|
+
],
|
|
3898
|
+
// the truncated tetrahedron's skeleton; 12 nodes, 18 edges, planar
|
|
3899
|
+
"truncated-tetrahedron": [
|
|
3900
|
+
12,
|
|
3901
|
+
[
|
|
3902
|
+
0,
|
|
3903
|
+
1,
|
|
3904
|
+
0,
|
|
3905
|
+
2,
|
|
3906
|
+
0,
|
|
3907
|
+
9,
|
|
3908
|
+
1,
|
|
3909
|
+
2,
|
|
3910
|
+
1,
|
|
3911
|
+
6,
|
|
3912
|
+
2,
|
|
3913
|
+
3,
|
|
3914
|
+
3,
|
|
3915
|
+
4,
|
|
3916
|
+
3,
|
|
3917
|
+
11,
|
|
3918
|
+
4,
|
|
3919
|
+
5,
|
|
3920
|
+
4,
|
|
3921
|
+
11,
|
|
3922
|
+
5,
|
|
3923
|
+
6,
|
|
3924
|
+
5,
|
|
3925
|
+
7,
|
|
3926
|
+
6,
|
|
3927
|
+
7,
|
|
3928
|
+
7,
|
|
3929
|
+
8,
|
|
3930
|
+
8,
|
|
3931
|
+
9,
|
|
3932
|
+
8,
|
|
3933
|
+
10,
|
|
3934
|
+
9,
|
|
3935
|
+
10,
|
|
3936
|
+
10,
|
|
3937
|
+
11
|
|
3938
|
+
]
|
|
3939
|
+
],
|
|
3940
|
+
// Tutte's graph: 3-regular, 3-connected, planar and not Hamiltonian; 46 nodes, 69 edges
|
|
3941
|
+
tutte: [
|
|
3942
|
+
46,
|
|
3943
|
+
[
|
|
3944
|
+
0,
|
|
3945
|
+
1,
|
|
3946
|
+
0,
|
|
3947
|
+
2,
|
|
3948
|
+
0,
|
|
3949
|
+
3,
|
|
3950
|
+
1,
|
|
3951
|
+
4,
|
|
3952
|
+
1,
|
|
3953
|
+
26,
|
|
3954
|
+
2,
|
|
3955
|
+
10,
|
|
3956
|
+
2,
|
|
3957
|
+
11,
|
|
3958
|
+
3,
|
|
3959
|
+
18,
|
|
3960
|
+
3,
|
|
3961
|
+
19,
|
|
3962
|
+
4,
|
|
3963
|
+
5,
|
|
3964
|
+
4,
|
|
3965
|
+
33,
|
|
3966
|
+
5,
|
|
3967
|
+
6,
|
|
3968
|
+
5,
|
|
3969
|
+
29,
|
|
3970
|
+
6,
|
|
3971
|
+
7,
|
|
3972
|
+
6,
|
|
3973
|
+
27,
|
|
3974
|
+
7,
|
|
3975
|
+
8,
|
|
3976
|
+
7,
|
|
3977
|
+
14,
|
|
3978
|
+
8,
|
|
3979
|
+
9,
|
|
3980
|
+
8,
|
|
3981
|
+
38,
|
|
3982
|
+
9,
|
|
3983
|
+
10,
|
|
3984
|
+
9,
|
|
3985
|
+
37,
|
|
3986
|
+
10,
|
|
3987
|
+
39,
|
|
3988
|
+
11,
|
|
3989
|
+
12,
|
|
3990
|
+
11,
|
|
3991
|
+
39,
|
|
3992
|
+
12,
|
|
3993
|
+
13,
|
|
3994
|
+
12,
|
|
3995
|
+
35,
|
|
3996
|
+
13,
|
|
3997
|
+
14,
|
|
3998
|
+
13,
|
|
3999
|
+
15,
|
|
4000
|
+
14,
|
|
4001
|
+
34,
|
|
4002
|
+
15,
|
|
4003
|
+
16,
|
|
4004
|
+
15,
|
|
4005
|
+
22,
|
|
4006
|
+
16,
|
|
4007
|
+
17,
|
|
4008
|
+
16,
|
|
4009
|
+
44,
|
|
4010
|
+
17,
|
|
4011
|
+
18,
|
|
4012
|
+
17,
|
|
4013
|
+
43,
|
|
4014
|
+
18,
|
|
4015
|
+
45,
|
|
4016
|
+
19,
|
|
4017
|
+
20,
|
|
4018
|
+
19,
|
|
4019
|
+
45,
|
|
4020
|
+
20,
|
|
4021
|
+
21,
|
|
4022
|
+
20,
|
|
4023
|
+
41,
|
|
4024
|
+
21,
|
|
4025
|
+
22,
|
|
4026
|
+
21,
|
|
4027
|
+
23,
|
|
4028
|
+
22,
|
|
4029
|
+
40,
|
|
4030
|
+
23,
|
|
4031
|
+
24,
|
|
4032
|
+
23,
|
|
4033
|
+
27,
|
|
4034
|
+
24,
|
|
4035
|
+
25,
|
|
4036
|
+
24,
|
|
4037
|
+
32,
|
|
4038
|
+
25,
|
|
4039
|
+
26,
|
|
4040
|
+
25,
|
|
4041
|
+
31,
|
|
4042
|
+
26,
|
|
4043
|
+
33,
|
|
4044
|
+
27,
|
|
4045
|
+
28,
|
|
4046
|
+
28,
|
|
4047
|
+
29,
|
|
4048
|
+
28,
|
|
4049
|
+
32,
|
|
4050
|
+
29,
|
|
4051
|
+
30,
|
|
4052
|
+
30,
|
|
4053
|
+
31,
|
|
4054
|
+
30,
|
|
4055
|
+
33,
|
|
4056
|
+
31,
|
|
4057
|
+
32,
|
|
4058
|
+
34,
|
|
4059
|
+
35,
|
|
4060
|
+
34,
|
|
4061
|
+
38,
|
|
4062
|
+
35,
|
|
4063
|
+
36,
|
|
4064
|
+
36,
|
|
4065
|
+
37,
|
|
4066
|
+
36,
|
|
4067
|
+
39,
|
|
4068
|
+
37,
|
|
4069
|
+
38,
|
|
4070
|
+
40,
|
|
4071
|
+
41,
|
|
4072
|
+
40,
|
|
4073
|
+
44,
|
|
4074
|
+
41,
|
|
4075
|
+
42,
|
|
4076
|
+
42,
|
|
4077
|
+
43,
|
|
4078
|
+
42,
|
|
4079
|
+
45,
|
|
4080
|
+
43,
|
|
4081
|
+
44
|
|
4082
|
+
]
|
|
4083
|
+
]
|
|
4084
|
+
};
|
|
4085
|
+
function namedGraph(name, options = {}) {
|
|
4086
|
+
const entry = Object.prototype.hasOwnProperty.call(GRAPHS, name) ? GRAPHS[name] : void 0;
|
|
4087
|
+
if (entry === void 0) {
|
|
4088
|
+
throw new RangeError(`unknown named graph "${name}"; see NAMED_GRAPH_NAMES`);
|
|
4089
|
+
}
|
|
4090
|
+
const [nodeCount, flat] = entry;
|
|
4091
|
+
const m = flat.length / 2;
|
|
4092
|
+
const src = new Uint32Array(m);
|
|
4093
|
+
const dst = new Uint32Array(m);
|
|
4094
|
+
for (let e = 0; e < m; e++) {
|
|
4095
|
+
src[e] = flat[2 * e];
|
|
4096
|
+
dst[e] = flat[2 * e + 1];
|
|
4097
|
+
}
|
|
4098
|
+
return applyWeights({ directed: false, nodeCount, src, dst }, options);
|
|
4099
|
+
}
|
|
4100
|
+
function barabasiAlbertGraph(options) {
|
|
4101
|
+
const { n, m } = options;
|
|
4102
|
+
const triad = options.triadProbability ?? 0;
|
|
4103
|
+
checkInt("m", m, 1);
|
|
4104
|
+
checkInt("n", n, m + 1);
|
|
4105
|
+
checkProbability("triadProbability", triad);
|
|
4106
|
+
const seed = resolveSeed(options.seed);
|
|
4107
|
+
const edgeCount = (n - m) * m;
|
|
4108
|
+
checkEdgeCount(edgeCount);
|
|
4109
|
+
const stream = new RandomStream(seed, "barabasi-albert", 0);
|
|
4110
|
+
const out = new EdgeBuffer(edgeCount);
|
|
4111
|
+
const repeated = new Uint32Array(m + 2 * edgeCount);
|
|
4112
|
+
let length = 0;
|
|
4113
|
+
for (let i = 0; i < m; i++) {
|
|
4114
|
+
repeated[length++] = i;
|
|
4115
|
+
}
|
|
4116
|
+
const adjacency = triad > 0 ? Array.from({ length: n }, () => []) : null;
|
|
4117
|
+
const targets = new Array(m);
|
|
4118
|
+
const link = (s, t) => {
|
|
4119
|
+
out.push(s, t);
|
|
4120
|
+
repeated[length++] = t;
|
|
4121
|
+
if (adjacency !== null) {
|
|
4122
|
+
adjacency[s].push(t);
|
|
4123
|
+
adjacency[t].push(s);
|
|
4124
|
+
}
|
|
4125
|
+
};
|
|
4126
|
+
for (let s = m; s < n; s++) {
|
|
4127
|
+
let found = 0;
|
|
4128
|
+
while (found < m) {
|
|
4129
|
+
const t = repeated[stream.nextBelow(length)];
|
|
4130
|
+
let fresh = true;
|
|
4131
|
+
for (let i = 0; i < found; i++) {
|
|
4132
|
+
fresh &&= targets[i] !== t;
|
|
4133
|
+
}
|
|
4134
|
+
if (fresh) {
|
|
4135
|
+
targets[found++] = t;
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
let next = 0;
|
|
4139
|
+
let target = targets[next++];
|
|
4140
|
+
link(s, target);
|
|
4141
|
+
for (let count = 1; count < m; count++) {
|
|
4142
|
+
if (adjacency !== null && stream.nextFloat() < triad) {
|
|
4143
|
+
const mine = adjacency[s];
|
|
4144
|
+
const candidates = adjacency[target].filter((x) => x !== s && !mine.includes(x));
|
|
4145
|
+
if (candidates.length > 0) {
|
|
4146
|
+
link(s, candidates[stream.nextBelow(candidates.length)]);
|
|
4147
|
+
continue;
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
do {
|
|
4151
|
+
target = targets[next++];
|
|
4152
|
+
} while (adjacency !== null && adjacency[s].includes(target) && next < m);
|
|
4153
|
+
if (adjacency !== null && adjacency[s].includes(target)) {
|
|
4154
|
+
do {
|
|
4155
|
+
target = repeated[stream.nextBelow(length)];
|
|
4156
|
+
} while (target === s || adjacency[s].includes(target));
|
|
4157
|
+
}
|
|
4158
|
+
link(s, target);
|
|
4159
|
+
}
|
|
4160
|
+
for (let i = 0; i < m; i++) {
|
|
4161
|
+
repeated[length++] = s;
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
4165
|
+
}
|
|
4166
|
+
function wattsStrogatzGraph(options) {
|
|
4167
|
+
const { n, k, beta } = options;
|
|
4168
|
+
checkInt("k", k, 2);
|
|
4169
|
+
if (k % 2 !== 0) {
|
|
4170
|
+
throw new RangeError(`k must be even, got ${k}`);
|
|
4171
|
+
}
|
|
4172
|
+
checkInt("n", n, k + 1);
|
|
4173
|
+
checkProbability("beta", beta);
|
|
4174
|
+
const seed = resolveSeed(options.seed);
|
|
4175
|
+
const half = k / 2;
|
|
4176
|
+
const m = n * half;
|
|
4177
|
+
checkEdgeCount(m);
|
|
4178
|
+
const src = new Uint32Array(m);
|
|
4179
|
+
const dst = new Uint32Array(m);
|
|
4180
|
+
const degree = new Uint32Array(n).fill(k);
|
|
4181
|
+
const edges = /* @__PURE__ */ new Set();
|
|
4182
|
+
const key = (a, b) => a < b ? a * n + b : b * n + a;
|
|
4183
|
+
for (let u = 0; u < n; u++) {
|
|
4184
|
+
for (let j = 1; j <= half; j++) {
|
|
4185
|
+
const v = (u + j) % n;
|
|
4186
|
+
src[u * half + j - 1] = u;
|
|
4187
|
+
dst[u * half + j - 1] = v;
|
|
4188
|
+
edges.add(key(u, v));
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
const stream = new RandomStream(seed, "watts-strogatz", 0);
|
|
4192
|
+
for (let j = 1; j <= half; j++) {
|
|
4193
|
+
for (let u = 0; u < n; u++) {
|
|
4194
|
+
if (stream.nextFloat() >= beta || degree[u] >= n - 1) {
|
|
4195
|
+
continue;
|
|
4196
|
+
}
|
|
4197
|
+
let w = stream.nextBelow(n);
|
|
4198
|
+
while (w === u || edges.has(key(u, w))) {
|
|
4199
|
+
w = stream.nextBelow(n);
|
|
4200
|
+
}
|
|
4201
|
+
const slot = u * half + j - 1;
|
|
4202
|
+
const v = dst[slot];
|
|
4203
|
+
edges.delete(key(u, v));
|
|
4204
|
+
edges.add(key(u, w));
|
|
4205
|
+
degree[v]--;
|
|
4206
|
+
degree[w]++;
|
|
4207
|
+
dst[slot] = w;
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
return applyWeights({ directed: false, nodeCount: n, src, dst }, options);
|
|
4211
|
+
}
|
|
4212
|
+
function randomTreeGraph(options) {
|
|
4213
|
+
const { n } = options;
|
|
4214
|
+
checkInt("n", n, 1);
|
|
4215
|
+
const seed = resolveSeed(options.seed);
|
|
4216
|
+
const out = new EdgeBuffer(n);
|
|
4217
|
+
if (n === 1) {
|
|
4218
|
+
return applyWeights(toGraph(1, out, false), options);
|
|
4219
|
+
}
|
|
4220
|
+
const stream = new RandomStream(seed, "prufer", 0);
|
|
4221
|
+
const code = new Uint32Array(n - 2);
|
|
4222
|
+
const degree = new Uint32Array(n).fill(1);
|
|
4223
|
+
for (let i = 0; i < n - 2; i++) {
|
|
4224
|
+
code[i] = stream.nextBelow(n);
|
|
4225
|
+
degree[code[i]]++;
|
|
4226
|
+
}
|
|
4227
|
+
let ptr = 0;
|
|
4228
|
+
while (degree[ptr] !== 1) {
|
|
4229
|
+
ptr++;
|
|
4230
|
+
}
|
|
4231
|
+
let leaf = ptr;
|
|
4232
|
+
for (let i = 0; i < n - 2; i++) {
|
|
4233
|
+
const v = code[i];
|
|
4234
|
+
out.push(leaf, v);
|
|
4235
|
+
degree[v]--;
|
|
4236
|
+
if (degree[v] === 1 && v < ptr) {
|
|
4237
|
+
leaf = v;
|
|
4238
|
+
} else {
|
|
4239
|
+
ptr++;
|
|
4240
|
+
while (degree[ptr] !== 1) {
|
|
4241
|
+
ptr++;
|
|
4242
|
+
}
|
|
4243
|
+
leaf = ptr;
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
out.push(leaf, n - 1);
|
|
4247
|
+
return applyWeights(toGraph(n, out, false), options);
|
|
4248
|
+
}
|
|
4249
|
+
function dagRows(starts, layerOf, p, seed, start, end, out) {
|
|
4250
|
+
const logQ = detLog(1 - p);
|
|
4251
|
+
const stream = new RandomStream(seed, "layered-dag", 0);
|
|
4252
|
+
const last = starts.length - 2;
|
|
4253
|
+
for (let u = start; u < end; u++) {
|
|
4254
|
+
const layer = layerOf[u];
|
|
4255
|
+
if (layer < last) {
|
|
4256
|
+
stream.reset(u);
|
|
4257
|
+
bernoulliSegment(stream, p, logQ, u, starts[layer + 1], starts[layer + 2], out);
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
function randomDagGraph(options) {
|
|
4262
|
+
const { layers, p } = options;
|
|
4263
|
+
checkInt("layers.length", layers.length, 1);
|
|
4264
|
+
checkProbability("p", p);
|
|
4265
|
+
const seed = resolveSeed(options.seed);
|
|
4266
|
+
const starts = [0];
|
|
4267
|
+
let pairs = 0;
|
|
4268
|
+
for (let i = 0; i < layers.length; i++) {
|
|
4269
|
+
checkInt(`layers[${i}]`, layers[i], 0);
|
|
4270
|
+
starts.push(starts[i] + layers[i]);
|
|
4271
|
+
if (i > 0) {
|
|
4272
|
+
pairs += layers[i - 1] * layers[i];
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
const n = starts[layers.length];
|
|
4276
|
+
checkInt("the total size", n, 0);
|
|
4277
|
+
const layerOf = new Uint32Array(n);
|
|
4278
|
+
for (let i = 0; i < layers.length; i++) {
|
|
4279
|
+
layerOf.fill(i, starts[i], starts[i + 1]);
|
|
4280
|
+
}
|
|
4281
|
+
const out = binomialBuffer(pairs, p);
|
|
4282
|
+
dagRows(starts, layerOf, p, seed, 0, n, out);
|
|
4283
|
+
return applyWeights(toGraph(n, out, true, { layer: layerOf }), options);
|
|
4284
|
+
}
|
|
4285
|
+
function randomMultigraph(options) {
|
|
4286
|
+
const { n, m } = options;
|
|
4287
|
+
const loops = options.selfLoops ?? true;
|
|
4288
|
+
checkInt("n", n, 1);
|
|
4289
|
+
checkInt("m", m, 0);
|
|
4290
|
+
if (!loops && n < 2) {
|
|
4291
|
+
throw new RangeError(`selfLoops: false needs n >= 2, got ${n}`);
|
|
4292
|
+
}
|
|
4293
|
+
const stream = new RandomStream(resolveSeed(options.seed), "multigraph", 0);
|
|
4294
|
+
const src = new Uint32Array(m);
|
|
4295
|
+
const dst = new Uint32Array(m);
|
|
4296
|
+
for (let e = 0; e < m; e++) {
|
|
4297
|
+
const u = stream.nextBelow(n);
|
|
4298
|
+
let v;
|
|
4299
|
+
if (loops) {
|
|
4300
|
+
v = stream.nextBelow(n);
|
|
4301
|
+
} else {
|
|
4302
|
+
v = stream.nextBelow(n - 1);
|
|
4303
|
+
if (v >= u) {
|
|
4304
|
+
v++;
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
src[e] = u;
|
|
4308
|
+
dst[e] = v;
|
|
4309
|
+
}
|
|
4310
|
+
return applyWeights({ directed: options.directed ?? false, nodeCount: n, src, dst }, options);
|
|
4311
|
+
}
|
|
4312
|
+
function addPathologicalEdges(graph, options) {
|
|
4313
|
+
const loops = options.selfLoops ?? 0;
|
|
4314
|
+
const parallel = options.parallelEdges ?? 0;
|
|
4315
|
+
const anti = options.antiParallelEdges ?? 0;
|
|
4316
|
+
checkInt("selfLoops", loops, 0);
|
|
4317
|
+
checkInt("parallelEdges", parallel, 0);
|
|
4318
|
+
checkInt("antiParallelEdges", anti, 0);
|
|
4319
|
+
if (anti > 0 && !graph.directed) {
|
|
4320
|
+
throw new RangeError("antiParallelEdges needs a directed graph");
|
|
4321
|
+
}
|
|
4322
|
+
const m = graph.src.length;
|
|
4323
|
+
if (loops > 0 && graph.nodeCount === 0) {
|
|
4324
|
+
throw new RangeError("selfLoops needs a graph with at least one node");
|
|
4325
|
+
}
|
|
4326
|
+
if (parallel + anti > 0 && m === 0) {
|
|
4327
|
+
throw new RangeError("parallelEdges and antiParallelEdges need a graph with at least one edge");
|
|
4328
|
+
}
|
|
4329
|
+
const total = m + loops + parallel + anti;
|
|
4330
|
+
checkEdgeCount(total);
|
|
4331
|
+
const stream = new RandomStream(resolveSeed(options.seed), "pathological", 0);
|
|
4332
|
+
const src = new Uint32Array(total);
|
|
4333
|
+
const dst = new Uint32Array(total);
|
|
4334
|
+
src.set(graph.src);
|
|
4335
|
+
dst.set(graph.dst);
|
|
4336
|
+
const weights = graph.weights === void 0 ? void 0 : new Float32Array(total);
|
|
4337
|
+
weights?.set(graph.weights ?? []);
|
|
4338
|
+
let e = m;
|
|
4339
|
+
for (let i = 0; i < loops; i++, e++) {
|
|
4340
|
+
const v = stream.nextBelow(graph.nodeCount);
|
|
4341
|
+
src[e] = v;
|
|
4342
|
+
dst[e] = v;
|
|
4343
|
+
if (weights !== void 0) {
|
|
4344
|
+
weights[e] = 1;
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
for (let i = 0; i < parallel + anti; i++, e++) {
|
|
4348
|
+
const f = stream.nextBelow(m);
|
|
4349
|
+
const reversed = i >= parallel;
|
|
4350
|
+
src[e] = reversed ? graph.dst[f] : graph.src[f];
|
|
4351
|
+
dst[e] = reversed ? graph.src[f] : graph.dst[f];
|
|
4352
|
+
if (weights !== void 0) {
|
|
4353
|
+
weights[e] = weights[f];
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
return { ...graph, src, dst, weights };
|
|
4357
|
+
}
|
|
4358
|
+
const EDGE_CASE_NAMES = [
|
|
4359
|
+
"empty",
|
|
4360
|
+
"single-node",
|
|
4361
|
+
"single-self-loop",
|
|
4362
|
+
"isolated-nodes",
|
|
4363
|
+
"disconnected",
|
|
4364
|
+
"pathological-undirected",
|
|
4365
|
+
"pathological-directed",
|
|
4366
|
+
"zero-weights",
|
|
4367
|
+
"negative-weights",
|
|
4368
|
+
"negative-cycle",
|
|
4369
|
+
"duplicate-heavy",
|
|
4370
|
+
"max-node-index",
|
|
4371
|
+
"star-100k",
|
|
4372
|
+
"path-100k"
|
|
4373
|
+
];
|
|
4374
|
+
function build(directed, nodeCount, edges, weighted = false) {
|
|
4375
|
+
const stride = weighted ? 3 : 2;
|
|
4376
|
+
const m = edges.length / stride;
|
|
4377
|
+
const src = new Uint32Array(m);
|
|
4378
|
+
const dst = new Uint32Array(m);
|
|
4379
|
+
const weights = new Float32Array(weighted ? m : 0);
|
|
4380
|
+
for (let e = 0; e < m; e++) {
|
|
4381
|
+
src[e] = edges[e * stride];
|
|
4382
|
+
dst[e] = edges[e * stride + 1];
|
|
4383
|
+
weights[e] = weighted ? edges[e * stride + 2] : 0;
|
|
4384
|
+
}
|
|
4385
|
+
return weighted ? { directed, nodeCount, src, dst, weights } : { directed, nodeCount, src, dst };
|
|
4386
|
+
}
|
|
4387
|
+
function generated(nodeCount, m, end) {
|
|
4388
|
+
const src = new Uint32Array(m);
|
|
4389
|
+
const dst = new Uint32Array(m);
|
|
4390
|
+
for (let e = 0; e < m; e++) {
|
|
4391
|
+
[src[e], dst[e]] = end(e);
|
|
4392
|
+
}
|
|
4393
|
+
return { directed: false, nodeCount, src, dst };
|
|
4394
|
+
}
|
|
4395
|
+
const CASES = {
|
|
4396
|
+
empty: () => build(false, 0, []),
|
|
4397
|
+
"single-node": () => build(false, 1, []),
|
|
4398
|
+
"single-self-loop": () => build(false, 1, [0, 0]),
|
|
4399
|
+
"isolated-nodes": () => build(false, 10, []),
|
|
4400
|
+
// triangle 0-1-2, path 3-4-5, edge 6-7, isolated 8 and 9
|
|
4401
|
+
disconnected: () => build(false, 10, [0, 1, 1, 2, 2, 0, 3, 4, 4, 5, 6, 7]),
|
|
4402
|
+
// self-loop at 0; (1, 2) three times, once written (2, 1); weights 0, negative and fractional
|
|
4403
|
+
"pathological-undirected": () => build(false, 4, [0, 0, 1, 1, 2, 0, 1, 2, -2, 2, 1, 0.5, 2, 3, 2.5, 3, 0, -1], true),
|
|
4404
|
+
// self-loop at 0; parallel arcs 0 -> 1; the 2-cycle 1 <-> 2 (an anti-parallel pair, one arc
|
|
4405
|
+
// of weight 0); the negative cycle 2 -> 3 -> 4 -> 2 of total weight -2, reachable from 0
|
|
4406
|
+
"pathological-directed": () => build(true, 5, [0, 0, 1, 0, 1, 2, 0, 1, 3, 1, 2, 1, 2, 1, 0, 2, 3, -1, 3, 4, -2, 4, 2, 1, 1, 3, 0.5], true),
|
|
4407
|
+
// a 4-cycle with the chord 0-2, every weight 0: ties everywhere for Dijkstra and MST
|
|
4408
|
+
"zero-weights": () => build(false, 4, [0, 1, 0, 1, 2, 0, 2, 3, 0, 3, 0, 0, 0, 2, 0], true),
|
|
4409
|
+
// a DAG with negative arcs and no negative cycle; distances from 0 are [0, 4, 1, 4, 3]
|
|
4410
|
+
"negative-weights": () => build(true, 5, [0, 1, 4, 0, 2, 2, 1, 2, -3, 1, 3, 2, 2, 3, 3, 3, 4, -1, 2, 4, 5], true),
|
|
4411
|
+
// 0 -> 1, then the cycle 1 -> 2 -> 3 -> 1 of total weight -1
|
|
4412
|
+
"negative-cycle": () => build(true, 4, [0, 1, 1, 1, 2, -1, 2, 3, -1, 3, 1, 1], true),
|
|
4413
|
+
// 1,000 copies of the edge (0, 1)
|
|
4414
|
+
"duplicate-heavy": () => generated(2, 1e3, () => [0, 1]),
|
|
4415
|
+
// 2^20 nodes, edges only at the ends: sparse index handling and large-id output
|
|
4416
|
+
"max-node-index": () => build(false, 2 ** 20, [0, 1, 2 ** 20 - 2, 2 ** 20 - 1, 0, 2 ** 20 - 1]),
|
|
4417
|
+
// hub 0 with 100,000 leaves
|
|
4418
|
+
"star-100k": () => generated(100001, 1e5, (e) => [0, e + 1]),
|
|
4419
|
+
// the path 0 - 1 - ... - 99,999
|
|
4420
|
+
"path-100k": () => generated(1e5, 99999, (e) => [e, e + 1])
|
|
4421
|
+
};
|
|
4422
|
+
function edgeCaseGraph(name) {
|
|
4423
|
+
if (!EDGE_CASE_NAMES.includes(name)) {
|
|
4424
|
+
throw new RangeError(`unknown edge case "${name}"; expected one of ${EDGE_CASE_NAMES.join(", ")}`);
|
|
4425
|
+
}
|
|
4426
|
+
return CASES[name]();
|
|
4427
|
+
}
|
|
4428
|
+
export {
|
|
4429
|
+
EDGE_CASE_NAMES,
|
|
4430
|
+
NAMED_GRAPH_NAMES,
|
|
4431
|
+
addPathologicalEdges,
|
|
4432
|
+
akGraph,
|
|
4433
|
+
balancedTreeGraph,
|
|
4434
|
+
barabasiAlbertGraph,
|
|
4435
|
+
barbellGraph,
|
|
4436
|
+
bianconiBarabasiGraph,
|
|
4437
|
+
bipartiteConfigurationModelGraph,
|
|
4438
|
+
cavemanGraph,
|
|
4439
|
+
chungLuGraph,
|
|
4440
|
+
circularLadderGraph,
|
|
4441
|
+
completeBipartiteGraph,
|
|
4442
|
+
completeGraph,
|
|
4443
|
+
completeMultipartiteGraph,
|
|
4444
|
+
configurationModelGraph,
|
|
4445
|
+
connectedCavemanGraph,
|
|
4446
|
+
cycleGraph,
|
|
4447
|
+
degreeCorrectedSbmGraph,
|
|
4448
|
+
directedConfigurationModelGraph,
|
|
4449
|
+
duplicationDivergenceGraph,
|
|
4450
|
+
edgeCaseGraph,
|
|
4451
|
+
emptyGraph,
|
|
4452
|
+
erdosRenyiGnmGraph,
|
|
4453
|
+
erdosRenyiGraph,
|
|
4454
|
+
forestFireGraph,
|
|
4455
|
+
genrmfGraph,
|
|
4456
|
+
grid3dGraph,
|
|
4457
|
+
gridFlowNetwork,
|
|
4458
|
+
gridGraph,
|
|
4459
|
+
hexagonalLatticeGraph,
|
|
4460
|
+
hyperbolicGraph,
|
|
4461
|
+
hypercubeGraph,
|
|
4462
|
+
knnGraph,
|
|
4463
|
+
kroneckerGraph,
|
|
4464
|
+
ladderGraph,
|
|
4465
|
+
layeredFlowNetwork,
|
|
4466
|
+
lfrGraph,
|
|
4467
|
+
lollipopGraph,
|
|
4468
|
+
mobiusLadderGraph,
|
|
4469
|
+
namedGraph,
|
|
4470
|
+
newmanWattsGraph,
|
|
4471
|
+
pathGraph,
|
|
4472
|
+
petersenGraph,
|
|
4473
|
+
plantedPartitionGraph,
|
|
4474
|
+
powerLawDegreeSequence,
|
|
4475
|
+
priceGraph,
|
|
4476
|
+
randomApollonianGraph,
|
|
4477
|
+
randomBipartiteGraph,
|
|
4478
|
+
randomDagGraph,
|
|
4479
|
+
randomGeometricGraph,
|
|
4480
|
+
randomMultigraph,
|
|
4481
|
+
randomOrderDagGraph,
|
|
4482
|
+
randomRecursiveTreeGraph,
|
|
4483
|
+
randomRegularGraph,
|
|
4484
|
+
randomTreeGraph,
|
|
4485
|
+
ringOfCliquesGraph,
|
|
4486
|
+
rmatGraph,
|
|
4487
|
+
starGraph,
|
|
4488
|
+
stochasticBlockModelGraph,
|
|
4489
|
+
triangularLatticeGraph,
|
|
4490
|
+
wattsStrogatzGraph,
|
|
4491
|
+
waxmanGraph,
|
|
4492
|
+
wheelGraph,
|
|
4493
|
+
wilsonMazeGraph,
|
|
4494
|
+
withWeights
|
|
4495
|
+
};
|
|
4496
|
+
//# sourceMappingURL=generators.js.map
|