@knowvah/dot-engine 2.6.11 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api.js +18 -4
- package/dist/api.js.map +3 -3
- package/dist/async/render-async.d.ts.map +1 -1
- package/dist/common/agerr.d.ts +37 -0
- package/dist/common/agerr.d.ts.map +1 -0
- package/dist/common/arrows-shapes-poly.d.ts.map +1 -1
- package/dist/common/color.d.ts.map +1 -1
- package/dist/common/crand.d.ts +0 -20
- package/dist/common/crand.d.ts.map +1 -1
- package/dist/common/emit-once.d.ts +6 -0
- package/dist/common/emit-once.d.ts.map +1 -0
- package/dist/common/graph-init.d.ts.map +1 -1
- package/dist/common/htmltable-attr-warn.d.ts.map +1 -1
- package/dist/common/htmltable-emit-fill.d.ts +29 -2
- package/dist/common/htmltable-emit-fill.d.ts.map +1 -1
- package/dist/common/htmltable-emit-rules.d.ts.map +1 -1
- package/dist/common/htmltable-emit.d.ts.map +1 -1
- package/dist/common/htmltable-lex-expat.d.ts +1 -6
- package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
- package/dist/common/htmltable-pos-runs.d.ts +7 -4
- package/dist/common/htmltable-pos-runs.d.ts.map +1 -1
- package/dist/common/htmltable.d.ts +1 -7
- package/dist/common/htmltable.d.ts.map +1 -1
- package/dist/common/libm-trig.d.ts +5 -0
- package/dist/common/libm-trig.d.ts.map +1 -0
- package/dist/common/multicolor.d.ts +21 -13
- package/dist/common/multicolor.d.ts.map +1 -1
- package/dist/common/poly-gencode.d.ts +1 -2
- package/dist/common/poly-gencode.d.ts.map +1 -1
- package/dist/common/poly-init.d.ts.map +1 -1
- package/dist/common/postproc.d.ts.map +1 -1
- package/dist/common/random.d.ts.map +1 -1
- package/dist/common/render-state.d.ts +8 -0
- package/dist/common/render-state.d.ts.map +1 -0
- package/dist/common/splines-clip.d.ts +7 -0
- package/dist/common/splines-clip.d.ts.map +1 -1
- package/dist/common/splines-geom.d.ts +8 -0
- package/dist/common/splines-geom.d.ts.map +1 -1
- package/dist/common/splines-routespl.d.ts.map +1 -1
- package/dist/common/textmeasure-lookup.d.ts.map +1 -1
- package/dist/common/textmeasure.d.ts.map +1 -1
- package/dist/gvc/anchor.d.ts.map +1 -1
- package/dist/gvc/context.d.ts +6 -0
- package/dist/gvc/context.d.ts.map +1 -1
- package/dist/gvc/device-cluster.d.ts +15 -0
- package/dist/gvc/device-cluster.d.ts.map +1 -1
- package/dist/gvc/device-labels.d.ts +14 -0
- package/dist/gvc/device-labels.d.ts.map +1 -1
- package/dist/gvc/device.d.ts.map +1 -1
- package/dist/gvc/first-layer.d.ts +9 -0
- package/dist/gvc/first-layer.d.ts.map +1 -0
- package/dist/gvc/gvrender-set-style.d.ts +28 -0
- package/dist/gvc/gvrender-set-style.d.ts.map +1 -0
- package/dist/gvc/usershape.d.ts +18 -0
- package/dist/gvc/usershape.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3169 -1296
- package/dist/index.js.map +4 -4
- package/dist/layout/dot/cluster.d.ts.map +1 -1
- package/dist/layout/dot/compound.d.ts.map +1 -1
- package/dist/layout/dot/conc.d.ts.map +1 -1
- package/dist/layout/dot/decomp.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-clip.d.ts +32 -14
- package/dist/layout/dot/edge-route-clip.d.ts.map +1 -1
- package/dist/layout/dot/edge-route-routing.d.ts.map +1 -1
- package/dist/layout/dot/mincross-build.d.ts.map +1 -1
- package/dist/layout/dot/mincross-cross.d.ts.map +1 -1
- package/dist/layout/dot/rank.d.ts.map +1 -1
- package/dist/layout/dot/splines.d.ts.map +1 -1
- package/dist/layout/fdp/index.d.ts.map +1 -1
- package/dist/layout/fdp/tlayout-parms.d.ts.map +1 -1
- package/dist/layout/fdp/xlayout.d.ts.map +1 -1
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts +11 -0
- package/dist/layout/neato/__fixtures__/dijkstra-f.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts +36 -0
- package/dist/layout/neato/__fixtures__/hier-imds.d.ts.map +1 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts +32 -0
- package/dist/layout/neato/__fixtures__/hier-majorization.d.ts.map +1 -0
- package/dist/layout/neato/cdt-surface.d.ts.map +1 -1
- package/dist/layout/neato/compute-hierarchy.d.ts +24 -0
- package/dist/layout/neato/compute-hierarchy.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts +20 -0
- package/dist/layout/neato/conjgrad-mkernel.d.ts.map +1 -0
- package/dist/layout/neato/conjgrad.d.ts +19 -0
- package/dist/layout/neato/conjgrad.d.ts.map +1 -1
- package/dist/layout/neato/constrained-majorization.d.ts +26 -0
- package/dist/layout/neato/constrained-majorization.d.ts.map +1 -0
- package/dist/layout/neato/dijkstra.d.ts +33 -16
- package/dist/layout/neato/dijkstra.d.ts.map +1 -1
- package/dist/layout/neato/edge-len.d.ts.map +1 -1
- package/dist/layout/neato/fdp-adjust.d.ts.map +1 -1
- package/dist/layout/neato/gts-triangle.d.ts +14 -0
- package/dist/layout/neato/gts-triangle.d.ts.map +1 -0
- package/dist/layout/neato/init.d.ts +6 -22
- package/dist/layout/neato/init.d.ts.map +1 -1
- package/dist/layout/neato/kk.d.ts.map +1 -1
- package/dist/layout/neato/kkutils.d.ts +20 -0
- package/dist/layout/neato/kkutils.d.ts.map +1 -0
- package/dist/layout/neato/make-graph-data.d.ts +43 -0
- package/dist/layout/neato/make-graph-data.d.ts.map +1 -0
- package/dist/layout/neato/matrix-ops.d.ts +46 -0
- package/dist/layout/neato/matrix-ops.d.ts.map +1 -1
- package/dist/layout/neato/multispline-router.d.ts.map +1 -1
- package/dist/layout/neato/multispline.d.ts.map +1 -1
- package/dist/layout/neato/opt-arrangement.d.ts +20 -0
- package/dist/layout/neato/opt-arrangement.d.ts.map +1 -0
- package/dist/layout/neato/poly.d.ts.map +1 -1
- package/dist/layout/neato/quad-prog-solve.d.ts +44 -0
- package/dist/layout/neato/quad-prog-solve.d.ts.map +1 -0
- package/dist/layout/neato/sgd.d.ts.map +1 -1
- package/dist/layout/neato/smart-ini-x.d.ts +9 -0
- package/dist/layout/neato/smart-ini-x.d.ts.map +1 -0
- package/dist/layout/neato/splines.d.ts +1 -1
- package/dist/layout/neato/splines.d.ts.map +1 -1
- package/dist/layout/neato/start.d.ts +4 -1
- package/dist/layout/neato/start.d.ts.map +1 -1
- package/dist/layout/neato/stress.d.ts +8 -0
- package/dist/layout/neato/stress.d.ts.map +1 -1
- package/dist/model/edge.d.ts +1 -1
- package/dist/model/edge.d.ts.map +1 -1
- package/dist/model/lazy-attrs.d.ts +10 -0
- package/dist/model/lazy-attrs.d.ts.map +1 -0
- package/dist/model/node.d.ts +1 -1
- package/dist/model/node.d.ts.map +1 -1
- package/dist/ortho/index.d.ts.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/render/color-resolve.d.ts +49 -1
- package/dist/render/color-resolve.d.ts.map +1 -1
- package/dist/render/dot/edge-draw.d.ts +1 -1
- package/dist/render/dot/edge-draw.d.ts.map +1 -1
- package/dist/render/public.d.ts.map +1 -1
- package/dist/render/svg-edge-split.d.ts +2 -0
- package/dist/render/svg-edge-split.d.ts.map +1 -1
- package/dist/render/svg-multicolor.d.ts +2 -2
- package/dist/render/svg-multicolor.d.ts.map +1 -1
- package/dist/render/xdot-public.d.ts.map +1 -1
- package/dist/render.js +2799 -927
- package/dist/render.js.map +4 -4
- package/package.json +1 -1
- package/src/async/render-async.ts +6 -0
- package/src/common/agerr.ts +96 -0
- package/src/common/arrows-shapes-poly.ts +19 -5
- package/src/common/color.ts +38 -10
- package/src/common/crand.ts +8 -2
- package/src/common/emit-once.ts +27 -0
- package/src/common/graph-init.ts +2 -1
- package/src/common/htmltable-attr-warn.ts +3 -4
- package/src/common/htmltable-emit-fill.ts +104 -11
- package/src/common/htmltable-emit-rules.ts +2 -0
- package/src/common/htmltable-emit.ts +4 -3
- package/src/common/htmltable-lex-expat.ts +96 -23
- package/src/common/htmltable-pos-runs.ts +11 -6
- package/src/common/htmltable.ts +28 -117
- package/src/common/libm-trig.ts +43 -0
- package/src/common/multicolor.ts +88 -24
- package/src/common/poly-gencode.ts +30 -10
- package/src/common/poly-init.ts +20 -1
- package/src/common/postproc.ts +4 -1
- package/src/common/random.ts +11 -1
- package/src/common/render-state.ts +36 -0
- package/src/common/splines-clip.ts +15 -12
- package/src/common/splines-geom.ts +8 -4
- package/src/common/splines-routespl.ts +2 -1
- package/src/common/textmeasure-lookup.ts +9 -2
- package/src/common/textmeasure.ts +45 -2
- package/src/gvc/anchor.ts +1 -12
- package/src/gvc/context.ts +8 -0
- package/src/gvc/device-cluster.ts +93 -10
- package/src/gvc/device-labels.ts +27 -0
- package/src/gvc/device.ts +34 -8
- package/src/gvc/first-layer.ts +26 -0
- package/src/gvc/gvrender-set-style.ts +114 -0
- package/src/gvc/usershape.ts +26 -0
- package/src/index.ts +3 -0
- package/src/layout/dot/cluster.ts +2 -1
- package/src/layout/dot/compound.ts +3 -4
- package/src/layout/dot/conc.ts +6 -5
- package/src/layout/dot/decomp.ts +3 -0
- package/src/layout/dot/edge-route-clip.ts +46 -28
- package/src/layout/dot/edge-route-routing.ts +11 -3
- package/src/layout/dot/fastgr.ts +2 -2
- package/src/layout/dot/mincross-build.ts +2 -0
- package/src/layout/dot/mincross-cross.ts +2 -0
- package/src/layout/dot/rank.ts +2 -0
- package/src/layout/dot/splines.ts +2 -1
- package/src/layout/fdp/index.ts +2 -1
- package/src/layout/fdp/tlayout-parms.ts +9 -2
- package/src/layout/fdp/xlayout.ts +10 -2
- package/src/layout/neato/__fixtures__/dijkstra-f.ts +17 -0
- package/src/layout/neato/__fixtures__/hier/c1554.gv +33 -0
- package/src/layout/neato/__fixtures__/hier/c1554.plain +26 -0
- package/src/layout/neato/__fixtures__/hier/c1554.xdot +161 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/cycle.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.plain +11 -0
- package/src/layout/neato/__fixtures__/hier/cases/dirnone.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/epsilon.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/levelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/mds.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/multiedge.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.plain +8 -0
- package/src/layout/neato/__fixtures__/hier/cases/neglevelsgap.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startrandom.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/startregular.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.plain +13 -0
- package/src/layout/neato/__fixtures__/hier/cases/tdist.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.plain +10 -0
- package/src/layout/neato/__fixtures__/hier/cases/undirected.stderr +0 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.gv +1 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.plain +16 -0
- package/src/layout/neato/__fixtures__/hier/hier-probe.xdot +62 -0
- package/src/layout/neato/__fixtures__/hier-imds.ts +151 -0
- package/src/layout/neato/__fixtures__/hier-majorization.ts +145 -0
- package/src/layout/neato/cdt-surface.ts +12 -22
- package/src/layout/neato/compute-hierarchy.ts +90 -0
- package/src/layout/neato/conjgrad-mkernel.ts +141 -0
- package/src/layout/neato/conjgrad.ts +208 -0
- package/src/layout/neato/constrained-majorization.ts +479 -0
- package/src/layout/neato/dijkstra.ts +218 -92
- package/src/layout/neato/edge-len.ts +2 -1
- package/src/layout/neato/fdp-adjust.ts +3 -2
- package/src/layout/neato/gts-triangle.ts +42 -0
- package/src/layout/neato/init.ts +68 -68
- package/src/layout/neato/kk.ts +2 -1
- package/src/layout/neato/kkutils.ts +96 -0
- package/src/layout/neato/make-graph-data.ts +166 -0
- package/src/layout/neato/matrix-ops.ts +138 -0
- package/src/layout/neato/multispline-router.ts +3 -3
- package/src/layout/neato/multispline.ts +3 -2
- package/src/layout/neato/opt-arrangement.ts +86 -0
- package/src/layout/neato/poly.ts +3 -2
- package/src/layout/neato/quad-prog-solve.ts +489 -0
- package/src/layout/neato/sgd.ts +5 -2
- package/src/layout/neato/smart-ini-x.ts +503 -0
- package/src/layout/neato/splines.ts +4 -4
- package/src/layout/neato/start.ts +19 -14
- package/src/layout/neato/stress.ts +28 -0
- package/src/model/edge.ts +3 -2
- package/src/model/lazy-attrs.ts +36 -0
- package/src/model/node.ts +3 -2
- package/src/ortho/index.ts +2 -1
- package/src/parser/dot.js +4 -1
- package/src/parser/dot.pegjs +5 -1
- package/src/parser/index.ts +4 -3
- package/src/render/color-resolve.ts +193 -4
- package/src/render/dot/edge-draw.ts +6 -3
- package/src/render/dot.ts +1 -1
- package/src/render/public.ts +2 -0
- package/src/render/svg-edge-split.ts +6 -4
- package/src/render/svg-multicolor.ts +4 -3
- package/src/render/svg-parallel-edge.ts +2 -2
- package/src/render/xdot-public.ts +2 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EPL-2.0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Port of neatogen/smart_ini_x.c: `IMDS_given_dim`, which finds the second
|
|
5
|
+
* coordinate of a hierarchical layout given the first. The inner products of
|
|
6
|
+
* this file are inlined by the 16.1 arm64 `-O3 -flto=thin` build at each call
|
|
7
|
+
* site, so every site carries its own unfused block width (see matrix-ops.ts);
|
|
8
|
+
* the constants below name them.
|
|
9
|
+
*
|
|
10
|
+
* @see lib/neatogen/smart_ini_x.c
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { crand } from '../../common/crand.js';
|
|
14
|
+
import { conjugateGradientF } from './conjgrad.js';
|
|
15
|
+
import type { VtxData } from './dijkstra.js';
|
|
16
|
+
import { computeApsp } from './kkutils.js';
|
|
17
|
+
import {
|
|
18
|
+
BLOCK_NONE,
|
|
19
|
+
copyVector,
|
|
20
|
+
fma,
|
|
21
|
+
innerProduct,
|
|
22
|
+
norm,
|
|
23
|
+
scadd,
|
|
24
|
+
vectorsScalarMult,
|
|
25
|
+
} from './matrix-ops.js';
|
|
26
|
+
|
|
27
|
+
const fr = Math.fround;
|
|
28
|
+
|
|
29
|
+
/** `#define SCALE_FACTOR` @see lib/neatogen/smart_ini_x.c */
|
|
30
|
+
const SCALE_FACTOR = 256;
|
|
31
|
+
|
|
32
|
+
/** `DBL_EPSILON` @see float.h */
|
|
33
|
+
const DBL_EPSILON = 2.220446049250313e-16;
|
|
34
|
+
|
|
35
|
+
/** The `len < 1e-10` colinearity / null-space test of power iteration. */
|
|
36
|
+
const POWER_LENGTH_FLOOR = 1e-10;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Unfused block widths per inlined call site of the 16.1 arm64 `-O3 -flto=thin`
|
|
40
|
+
* build (`BLOCK_NONE` = plain scalar loop). Calibrated bit for bit against the
|
|
41
|
+
* oracle's own libneatogen.a on random DAGs (n = 5..40, 192 graphs): each width
|
|
42
|
+
* was the only one of {1, 4, 8, 16} matching the native intermediate values.
|
|
43
|
+
* Sites inside the eigenvector loops over earlier eigenvectors (j < i) are
|
|
44
|
+
* never reached: CMDS_orthog asks for one eigenvector.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/** standardize: the norm of the centred vector. */
|
|
48
|
+
const BLOCK_STANDARDIZE_NORM = 4;
|
|
49
|
+
/** mat_mult_vec_orthog: each row of mat * vec. */
|
|
50
|
+
const BLOCK_MAT_MULT_ROW = 4;
|
|
51
|
+
/** mat_mult_vec_orthog: result . orthog. */
|
|
52
|
+
const BLOCK_MAT_MULT_ORTHOG = 8;
|
|
53
|
+
/** power iteration: orthog . guess. */
|
|
54
|
+
const BLOCK_GUESS_ORTHOG = 8;
|
|
55
|
+
/** power iteration: norm of the orthogonalized guess (scalar loop). */
|
|
56
|
+
const BLOCK_GUESS_LEN = BLOCK_NONE;
|
|
57
|
+
/** power iteration: norm inside the iteration (scalar loop). */
|
|
58
|
+
const BLOCK_LOOP_LEN = BLOCK_NONE;
|
|
59
|
+
/** power iteration: curr . last. */
|
|
60
|
+
const BLOCK_ANGLE = 8;
|
|
61
|
+
/**
|
|
62
|
+
* IMDS_given_dim: the sum1/sum2 loops. The disassembly shows a 16-wide body
|
|
63
|
+
* and a 4-wide epilogue; on the harness data 4 and a scalar loop agree.
|
|
64
|
+
*/
|
|
65
|
+
const BLOCK_UNIFORM_SUMS = 4;
|
|
66
|
+
/** compute_avgs: sums of squared int distances (exact below 2^26, so unobservable). */
|
|
67
|
+
const BLOCK_AVGS = 4;
|
|
68
|
+
|
|
69
|
+
/** Dense float matrix (`float **`), one `Float32Array` per row. */
|
|
70
|
+
type FloatMatrix = Float32Array[];
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Centre `orthog` and scale it to unit length (left alone when degenerate).
|
|
74
|
+
* @see lib/neatogen/smart_ini_x.c:standardize
|
|
75
|
+
*/
|
|
76
|
+
function standardize(orthog: Float64Array, nvtxs: number): void {
|
|
77
|
+
let avg = 0;
|
|
78
|
+
for (let i = 0; i < nvtxs; i++) avg += orthog[i]!;
|
|
79
|
+
avg /= nvtxs;
|
|
80
|
+
|
|
81
|
+
/* centralize: */
|
|
82
|
+
for (let i = 0; i < nvtxs; i++) orthog[i] = orthog[i]! - avg;
|
|
83
|
+
|
|
84
|
+
/* normalize: */
|
|
85
|
+
const len = norm(orthog, nvtxs - 1, BLOCK_STANDARDIZE_NORM);
|
|
86
|
+
|
|
87
|
+
// if we have a degenerate length, do not attempt to scale by it
|
|
88
|
+
if (Math.abs(len) < DBL_EPSILON) return;
|
|
89
|
+
|
|
90
|
+
vectorsScalarMult(nvtxs, orthog, 1.0 / len, orthog);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* result = (I - orthog orthog^T) mat vec, `mat` being dim1 x dim2.
|
|
95
|
+
* @see lib/neatogen/smart_ini_x.c:mat_mult_vec_orthog
|
|
96
|
+
*/
|
|
97
|
+
function matMultVecOrthog(
|
|
98
|
+
mat: FloatMatrix,
|
|
99
|
+
dim: number,
|
|
100
|
+
vec: Float64Array,
|
|
101
|
+
result: Float64Array,
|
|
102
|
+
orthog: Float64Array,
|
|
103
|
+
): void {
|
|
104
|
+
/* computes mat*vec, where mat is a dim*dim matrix */
|
|
105
|
+
const blocked = dim - (dim % BLOCK_MAT_MULT_ROW);
|
|
106
|
+
for (let i = 0; i < dim; i++) {
|
|
107
|
+
const row = mat[i]!;
|
|
108
|
+
let sum = 0;
|
|
109
|
+
let j = 0;
|
|
110
|
+
for (; j < blocked; j++) sum += row[j]! * vec[j]!;
|
|
111
|
+
for (; j < dim; j++) sum = fma(row[j]!, vec[j]!, sum);
|
|
112
|
+
result[i] = sum;
|
|
113
|
+
}
|
|
114
|
+
const alpha = -innerProduct(dim, result, orthog, BLOCK_MAT_MULT_ORTHOG);
|
|
115
|
+
scadd(result, dim - 1, alpha, orthog);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Inputs of power iteration shared by its phases. */
|
|
119
|
+
interface PowerIteration {
|
|
120
|
+
mat: FloatMatrix;
|
|
121
|
+
n: number;
|
|
122
|
+
eigs: Float64Array[];
|
|
123
|
+
evals: Float64Array;
|
|
124
|
+
orthog: Float64Array;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Orthogonalize `vec` against the first `count` eigenvectors.
|
|
129
|
+
* @see lib/neatogen/smart_ini_x.c:power_iteration_orthog
|
|
130
|
+
*/
|
|
131
|
+
function orthogonalizeAgainstEigs(
|
|
132
|
+
p: PowerIteration,
|
|
133
|
+
vec: Float64Array,
|
|
134
|
+
count: number,
|
|
135
|
+
block: number,
|
|
136
|
+
): void {
|
|
137
|
+
for (let j = 0; j < count; j++) {
|
|
138
|
+
const alpha = -innerProduct(p.n, p.eigs[j]!, vec, block);
|
|
139
|
+
scadd(vec, p.n - 1, alpha, p.eigs[j]!);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Guess the i-th eigenvector (`choose:` in C): random, orthogonal to `orthog`
|
|
145
|
+
* and the earlier eigenvectors, normalized; retried while nearly colinear.
|
|
146
|
+
* @see lib/neatogen/smart_ini_x.c:power_iteration_orthog
|
|
147
|
+
*/
|
|
148
|
+
function chooseStartVector(p: PowerIteration, i: number): void {
|
|
149
|
+
const curr = p.eigs[i]!;
|
|
150
|
+
for (;;) {
|
|
151
|
+
for (let j = 0; j < p.n; j++) curr[j] = crand() % 100;
|
|
152
|
+
const alpha = -innerProduct(p.n, p.orthog, curr, BLOCK_GUESS_ORTHOG);
|
|
153
|
+
scadd(curr, p.n - 1, alpha, p.orthog);
|
|
154
|
+
// orthogonalize against higher eigenvectors
|
|
155
|
+
orthogonalizeAgainstEigs(p, curr, i, BLOCK_NONE);
|
|
156
|
+
const len = norm(curr, p.n - 1, BLOCK_GUESS_LEN);
|
|
157
|
+
if (len < POWER_LENGTH_FLOOR) continue; // colinear with previous ones: goto choose
|
|
158
|
+
vectorsScalarMult(p.n, curr, 1.0 / len, curr);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Result of one eigenvector's power iteration. */
|
|
164
|
+
interface IterationResult {
|
|
165
|
+
/** true when the null space was reached (C `goto exit`). */
|
|
166
|
+
nullSpace: boolean;
|
|
167
|
+
eval: number;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Iterate `curr` to the dominant eigenvector (`do { } while` in C).
|
|
172
|
+
* @see lib/neatogen/smart_ini_x.c:power_iteration_orthog
|
|
173
|
+
*/
|
|
174
|
+
function iterateEigenvector(
|
|
175
|
+
p: PowerIteration,
|
|
176
|
+
i: number,
|
|
177
|
+
tol: number,
|
|
178
|
+
work: { tmp: Float64Array; last: Float64Array },
|
|
179
|
+
): IterationResult {
|
|
180
|
+
const curr = p.eigs[i]!;
|
|
181
|
+
for (;;) {
|
|
182
|
+
copyVector(p.n, curr, work.last);
|
|
183
|
+
matMultVecOrthog(p.mat, p.n, curr, work.tmp, p.orthog);
|
|
184
|
+
copyVector(p.n, work.tmp, curr);
|
|
185
|
+
/* orthogonalize against higher eigenvectors */
|
|
186
|
+
orthogonalizeAgainstEigs(p, curr, i, BLOCK_NONE);
|
|
187
|
+
const len = norm(curr, p.n - 1, BLOCK_LOOP_LEN);
|
|
188
|
+
if (len < POWER_LENGTH_FLOOR) {
|
|
189
|
+
/* We have reached the null space (e.vec. associated with e.val. 0) */
|
|
190
|
+
return { nullSpace: true, eval: 0 };
|
|
191
|
+
}
|
|
192
|
+
vectorsScalarMult(p.n, curr, 1.0 / len, curr);
|
|
193
|
+
const angle = innerProduct(p.n, curr, work.last, BLOCK_ANGLE);
|
|
194
|
+
if (!(Math.abs(angle) < tol)) {
|
|
195
|
+
/* the Rayleigh quotient (up to errors due to orthogonalization) */
|
|
196
|
+
return { nullSpace: false, eval: angle * len };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Remaining eigenvectors, probably of eigenvalue 0, for which power
|
|
203
|
+
* iteration is dangerous: random, orthogonalized, normalized, eval 0.
|
|
204
|
+
* @see lib/neatogen/smart_ini_x.c:power_iteration_orthog
|
|
205
|
+
*/
|
|
206
|
+
function fillNullSpaceEigenvectors(p: PowerIteration, from: number, neigs: number): void {
|
|
207
|
+
for (let i = from; i < neigs; i++) {
|
|
208
|
+
const curr = p.eigs[i]!;
|
|
209
|
+
/* guess the i-th eigen vector */
|
|
210
|
+
for (let j = 0; j < p.n; j++) curr[j] = crand() % 100;
|
|
211
|
+
/* orthogonalize against higher eigenvectors */
|
|
212
|
+
orthogonalizeAgainstEigs(p, curr, i, BLOCK_NONE);
|
|
213
|
+
const len = norm(curr, p.n - 1, BLOCK_NONE);
|
|
214
|
+
vectorsScalarMult(p.n, curr, 1.0 / len, curr);
|
|
215
|
+
p.evals[i] = 0;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sort the vectors by decreasing eigenvalue, against possible mis-convergence.
|
|
221
|
+
* @see lib/neatogen/smart_ini_x.c:power_iteration_orthog
|
|
222
|
+
*/
|
|
223
|
+
function sortEigenvectors(p: PowerIteration, neigs: number, tmp: Float64Array): void {
|
|
224
|
+
for (let i = 0; i < neigs - 1; i++) {
|
|
225
|
+
let largestIndex = i;
|
|
226
|
+
let largestEval = p.evals[largestIndex]!;
|
|
227
|
+
for (let j = i + 1; j < neigs; j++) {
|
|
228
|
+
if (largestEval < p.evals[j]!) {
|
|
229
|
+
largestIndex = j;
|
|
230
|
+
largestEval = p.evals[largestIndex]!;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (largestIndex !== i) { // exchange eigenvectors:
|
|
234
|
+
copyVector(p.n, p.eigs[i]!, tmp);
|
|
235
|
+
copyVector(p.n, p.eigs[largestIndex]!, p.eigs[i]!);
|
|
236
|
+
copyVector(p.n, tmp, p.eigs[largestIndex]!);
|
|
237
|
+
|
|
238
|
+
p.evals[largestIndex] = p.evals[i]!;
|
|
239
|
+
p.evals[i] = largestEval;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Power iteration with (I - orthog orthog^T) mat (I - orthog orthog^T).
|
|
246
|
+
* @see lib/neatogen/smart_ini_x.c:power_iteration_orthog
|
|
247
|
+
*/
|
|
248
|
+
function powerIterationOrthog(
|
|
249
|
+
p: PowerIteration,
|
|
250
|
+
neigsIn: number,
|
|
251
|
+
pIterationThreshold: number,
|
|
252
|
+
): void {
|
|
253
|
+
const work = { tmp: new Float64Array(p.n), last: new Float64Array(p.n) };
|
|
254
|
+
const tol = 1 - pIterationThreshold;
|
|
255
|
+
const neigs = neigsIn >= p.n ? p.n : neigsIn;
|
|
256
|
+
|
|
257
|
+
let i = 0;
|
|
258
|
+
for (; i < neigs; i++) {
|
|
259
|
+
chooseStartVector(p, i);
|
|
260
|
+
const res = iterateEigenvector(p, i, tol, work);
|
|
261
|
+
if (res.nullSpace) break; // goto exit (i is not advanced)
|
|
262
|
+
p.evals[i] = res.eval;
|
|
263
|
+
}
|
|
264
|
+
fillNullSpaceEigenvectors(p, i, neigs);
|
|
265
|
+
sortEigenvectors(p, neigs, work.tmp);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Row and overall averages of the squared distances, as floats.
|
|
270
|
+
* @see lib/neatogen/smart_ini_x.c:compute_avgs
|
|
271
|
+
*/
|
|
272
|
+
function computeAvgs(Dij: Int32Array[], n: number): { rowAvg: Float32Array; allAvg: number } {
|
|
273
|
+
const rowAvg = new Float32Array(n);
|
|
274
|
+
let sum = 0;
|
|
275
|
+
const blocked = n - (n % BLOCK_AVGS);
|
|
276
|
+
|
|
277
|
+
for (let i = 0; i < n; i++) {
|
|
278
|
+
let sumRow = 0;
|
|
279
|
+
const row = Dij[i]!;
|
|
280
|
+
let j = 0;
|
|
281
|
+
for (; j < blocked; j++) {
|
|
282
|
+
sum += row[j]! * row[j]!;
|
|
283
|
+
sumRow += row[j]! * row[j]!;
|
|
284
|
+
}
|
|
285
|
+
for (; j < n; j++) {
|
|
286
|
+
sum = fma(row[j]!, row[j]!, sum);
|
|
287
|
+
sumRow = fma(row[j]!, row[j]!, sumRow);
|
|
288
|
+
}
|
|
289
|
+
rowAvg[i] = fr(fr(sumRow) / n);
|
|
290
|
+
}
|
|
291
|
+
return { rowAvg, allAvg: fr(fr(sum) / (n * n)) };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The double-centred squared-distance matrix, as floats.
|
|
296
|
+
* @see lib/neatogen/smart_ini_x.c:compute_Bij
|
|
297
|
+
*/
|
|
298
|
+
function computeBij(Dij: Int32Array[], n: number): FloatMatrix {
|
|
299
|
+
const Bij: FloatMatrix = [];
|
|
300
|
+
for (let i = 0; i < n; i++) Bij.push(new Float32Array(n));
|
|
301
|
+
|
|
302
|
+
const { rowAvg, allAvg } = computeAvgs(Dij, n);
|
|
303
|
+
for (let i = 0; i < n; i++) {
|
|
304
|
+
for (let j = 0; j <= i; j++) {
|
|
305
|
+
// -(float)D*D + row_avg[i] contracts to fmsub (float), then two float ops
|
|
306
|
+
const fD = fr(Dij[i]![j]!);
|
|
307
|
+
const t = fr(fma(-fD, fD, rowAvg[i]!));
|
|
308
|
+
Bij[i]![j] = fr(fr(t + rowAvg[j]!) - allAvg);
|
|
309
|
+
Bij[j]![i] = Bij[i]![j]!;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return Bij;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Classical MDS in one dimension, orthogonal to `orthog`.
|
|
317
|
+
* @see lib/neatogen/smart_ini_x.c:CMDS_orthog
|
|
318
|
+
*/
|
|
319
|
+
function cmdsOrthog(
|
|
320
|
+
n: number,
|
|
321
|
+
dim: number,
|
|
322
|
+
eigs: Float64Array[],
|
|
323
|
+
tol: number,
|
|
324
|
+
input: { orthog: Float64Array; Dij: Int32Array[] },
|
|
325
|
+
): void {
|
|
326
|
+
const { orthog, Dij } = input;
|
|
327
|
+
const Bij = computeBij(Dij, n);
|
|
328
|
+
const evals = new Float64Array(dim);
|
|
329
|
+
|
|
330
|
+
const orthogAux = Float64Array.from(orthog.subarray(0, n));
|
|
331
|
+
standardize(orthogAux, n);
|
|
332
|
+
powerIterationOrthog({ mat: Bij, n, eigs, evals, orthog: orthogAux }, dim, tol);
|
|
333
|
+
|
|
334
|
+
for (let i = 0; i < dim; i++) {
|
|
335
|
+
for (let j = 0; j < n; j++) eigs[i]![j] = eigs[i]![j]! * Math.sqrt(Math.abs(evals[i]!));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Scale `x` so that distances along it best fit the ideal ones; returns the
|
|
341
|
+
* scale (`uniLength`, 0 when the sums overflow).
|
|
342
|
+
* @see lib/neatogen/smart_ini_x.c:IMDS_given_dim
|
|
343
|
+
*/
|
|
344
|
+
function scaleGivenCoords(Dij: Int32Array[], n: number, x: Float64Array): number {
|
|
345
|
+
let sum1 = 0;
|
|
346
|
+
let sum2 = 0;
|
|
347
|
+
for (let i = 1; i < n; i++) {
|
|
348
|
+
const blocked = i - (i % BLOCK_UNIFORM_SUMS);
|
|
349
|
+
for (let j = 0; j < i; j++) {
|
|
350
|
+
const d = Dij[i]![j]!;
|
|
351
|
+
const f = Math.abs(x[i]! - x[j]!);
|
|
352
|
+
const t1 = 1.0 / d;
|
|
353
|
+
const t2 = (1.0 / Math.imul(d, d)) * f;
|
|
354
|
+
if (j < blocked) {
|
|
355
|
+
sum1 += t1 * f;
|
|
356
|
+
sum2 += t2 * f;
|
|
357
|
+
} else {
|
|
358
|
+
sum1 = fma(t1, f, sum1);
|
|
359
|
+
sum2 = fma(t2, f, sum2);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const uniLength = Math.abs(sum2) === Infinity ? 0 : sum1 / sum2;
|
|
364
|
+
for (let i = 0; i < n; i++) x[i] = x[i]! * uniLength;
|
|
365
|
+
return uniLength;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The Laplacian of the weights w_ij = 1 / D_ij^2, as floats.
|
|
370
|
+
* @see lib/neatogen/smart_ini_x.c:IMDS_given_dim
|
|
371
|
+
*/
|
|
372
|
+
function computeLaplacian(Dij: Int32Array[], n: number): FloatMatrix {
|
|
373
|
+
const lap: FloatMatrix = [];
|
|
374
|
+
for (let i = 0; i < n; i++) {
|
|
375
|
+
const row = new Float32Array(n);
|
|
376
|
+
let degree = fr(0);
|
|
377
|
+
for (let j = 0; j < n; j++) {
|
|
378
|
+
if (j === i) continue;
|
|
379
|
+
const w = fr(fr(-1.0) / fr(fr(Dij[i]![j]!) * fr(Dij[i]![j]!))); // w_{ij}
|
|
380
|
+
row[j] = w;
|
|
381
|
+
degree = fr(degree - w);
|
|
382
|
+
}
|
|
383
|
+
row[i] = degree;
|
|
384
|
+
lap.push(row);
|
|
385
|
+
}
|
|
386
|
+
return lap;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Replace the distances by their residuals after removing the x component.
|
|
391
|
+
* @see lib/neatogen/smart_ini_x.c:IMDS_given_dim
|
|
392
|
+
*/
|
|
393
|
+
function computeResidualDistances(Dij: Int32Array[], n: number, x: Float64Array): void {
|
|
394
|
+
for (let i = 1; i < n; i++) {
|
|
395
|
+
const posI = x[i]!;
|
|
396
|
+
for (let j = 0; j < i; j++) {
|
|
397
|
+
const t = posI - x[j]!;
|
|
398
|
+
const d = Dij[i]![j]!;
|
|
399
|
+
const diff = fma(d, d, -(t * t));
|
|
400
|
+
const v = diff > 0 ? Math.sqrt(diff) : 0;
|
|
401
|
+
Dij[i]![j] = v;
|
|
402
|
+
Dij[j]![i] = v;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* One entry of the balance vector: sum_j sign * delta_ij * w_ij, the sign
|
|
409
|
+
* following the order of y_i and y_j.
|
|
410
|
+
* @see lib/neatogen/smart_ini_x.c:IMDS_given_dim
|
|
411
|
+
*/
|
|
412
|
+
function balanceOf(i: number, n: number, y: Float64Array, ctx: BalanceCtx): number {
|
|
413
|
+
const posI = y[i]!;
|
|
414
|
+
let b = 0;
|
|
415
|
+
for (let j = 0; j < n; j++) {
|
|
416
|
+
if (j === i) continue;
|
|
417
|
+
const term = fr(fr(ctx.Dij[i]![j]!) * -ctx.lap[i]![j]!); // w_{ij}*delta_{ij}
|
|
418
|
+
if (posI >= y[j]!) b += term;
|
|
419
|
+
else b -= term;
|
|
420
|
+
}
|
|
421
|
+
return b;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** Distances and Laplacian read by {@link balanceOf}. */
|
|
425
|
+
interface BalanceCtx {
|
|
426
|
+
Dij: Int32Array[];
|
|
427
|
+
lap: FloatMatrix;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Alternate conjugate gradient and balance updates until the balance is stable.
|
|
432
|
+
* Returns 1 when conjugate gradient fails (C tests `< 0`, which it never is).
|
|
433
|
+
* @see lib/neatogen/smart_ini_x.c:IMDS_given_dim
|
|
434
|
+
*/
|
|
435
|
+
function solveBalanced(
|
|
436
|
+
ctx: BalanceCtx,
|
|
437
|
+
n: number,
|
|
438
|
+
y: Float64Array,
|
|
439
|
+
balance: Float64Array,
|
|
440
|
+
convTol: number,
|
|
441
|
+
): 0 | 1 {
|
|
442
|
+
let converged = false;
|
|
443
|
+
for (let iterations2 = 0; iterations2 < 200 && !converged; iterations2++) {
|
|
444
|
+
if (conjugateGradientF(ctx.lap, y, balance, n, { tol: convTol, maxIterations: n, ortho1: true }) < 0) return 1;
|
|
445
|
+
converged = true;
|
|
446
|
+
for (let i = 0; i < n; i++) {
|
|
447
|
+
const b = balanceOf(i, n, y, ctx);
|
|
448
|
+
if (b !== balance[i] && Math.abs(1 - b / balance[i]!) > 1e-5) {
|
|
449
|
+
converged = false;
|
|
450
|
+
balance[i] = b;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Compute `newCoords` (y) given `givenCoords` (x) for a hierarchical layout;
|
|
459
|
+
* `givenCoords` is rescaled in place and restored at the end. Returns 1 when
|
|
460
|
+
* conjugate gradient fails.
|
|
461
|
+
* @see lib/neatogen/smart_ini_x.c:IMDS_given_dim
|
|
462
|
+
*/
|
|
463
|
+
export function imdsGivenDim(
|
|
464
|
+
graph: VtxData[],
|
|
465
|
+
n: number,
|
|
466
|
+
givenCoords: Float64Array,
|
|
467
|
+
newCoords: Float64Array,
|
|
468
|
+
conjTol: number,
|
|
469
|
+
): 0 | 1 {
|
|
470
|
+
const x = givenCoords;
|
|
471
|
+
const y = newCoords;
|
|
472
|
+
const Dij = computeApsp(graph, n);
|
|
473
|
+
|
|
474
|
+
/* scaling up the distances to enable an 'sqrt' operation later
|
|
475
|
+
* (in case distances are integers) */
|
|
476
|
+
for (let i = 0; i < n; i++) {
|
|
477
|
+
for (let j = 0; j < n; j++) Dij[i]![j] = Dij[i]![j]! * SCALE_FACTOR;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const uniLength = scaleGivenCoords(Dij, n, x);
|
|
481
|
+
|
|
482
|
+
/* smart ini: */
|
|
483
|
+
cmdsOrthog(n, 1, [y], conjTol, { orthog: x, Dij });
|
|
484
|
+
|
|
485
|
+
/* Compute Laplacian: */
|
|
486
|
+
const lap = computeLaplacian(Dij, n);
|
|
487
|
+
|
|
488
|
+
/* compute residual distances */
|
|
489
|
+
computeResidualDistances(Dij, n, x);
|
|
490
|
+
|
|
491
|
+
/* Compute the balance vector: */
|
|
492
|
+
const ctx: BalanceCtx = { Dij, lap };
|
|
493
|
+
const balance = new Float64Array(n);
|
|
494
|
+
for (let i = 0; i < n; i++) balance[i] = balanceOf(i, n, y, ctx);
|
|
495
|
+
|
|
496
|
+
if (solveBalanced(ctx, n, y, balance, conjTol) === 1) return 1;
|
|
497
|
+
|
|
498
|
+
for (let i = 0; !(Math.abs(uniLength) < DBL_EPSILON) && i < n; i++) {
|
|
499
|
+
x[i] = x[i]! / uniLength;
|
|
500
|
+
y[i] = y[i]! / uniLength;
|
|
501
|
+
}
|
|
502
|
+
return 0;
|
|
503
|
+
}
|
|
@@ -21,8 +21,8 @@ import type { SplineInfo, PolygonT, TextlabelT } from '../../common/types.js';
|
|
|
21
21
|
import { polygonOutlineRing } from '../../common/poly-sizing.js';
|
|
22
22
|
import { fma, fms } from '../../common/fma.js';
|
|
23
23
|
import { ShapeKind } from '../../common/types.js';
|
|
24
|
-
import type
|
|
25
|
-
import {
|
|
24
|
+
import { esepFactor, type ExpandT } from './sep-factor.js';
|
|
25
|
+
import { libmCos, libmSin } from '../../common/libm-trig.js';
|
|
26
26
|
import type { Poly, VConfig } from '../../pathplan/types.js';
|
|
27
27
|
import {
|
|
28
28
|
obsOpen, obsClose, obsPath,
|
|
@@ -138,8 +138,8 @@ function ellipseTangentSlope(a: number, b: number, p: Point): number {
|
|
|
138
138
|
function circumscribedCorner(a: number, b: number, i: number, nsides: number): Point {
|
|
139
139
|
const angle0 = (2 * Math.PI * (i - 0.5)) / nsides;
|
|
140
140
|
const angle1 = (2 * Math.PI * (i + 0.5)) / nsides;
|
|
141
|
-
const p0 = { x: a *
|
|
142
|
-
const p1 = { x: a *
|
|
141
|
+
const p0 = { x: a * libmCos(angle0), y: b * libmSin(angle0) }; // host libm
|
|
142
|
+
const p1 = { x: a * libmCos(angle1), y: b * libmSin(angle1) };
|
|
143
143
|
const m0 = ellipseTangentSlope(a, b, p0);
|
|
144
144
|
const m1 = ellipseTangentSlope(a, b, p1);
|
|
145
145
|
// line_intersection, contracted as the binary: fnmsub, fmls, +; y = fmadd.
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Neato `start` handling (setSeed / initRegular / checkStart) and the
|
|
4
4
|
* fail-loud checks for the neato init features this port does not have
|
|
5
|
-
* (smart init, the subset/circuit distance models,
|
|
5
|
+
* (smart init for the stress path, the subset/circuit distance models, ipsep
|
|
6
|
+
* with constraints). mode=hier runs `stress_majorization_with_hierarchy`; its
|
|
7
|
+
* own unported branches (subset/circuit models, the smart-init fallback
|
|
8
|
+
* without a hierarchy) are loud in constrained-majorization.ts.
|
|
6
9
|
*
|
|
7
10
|
* Each loud check sits where neato's C branches into the unported code and
|
|
8
11
|
* fires only for the modes whose C path reaches it (ADR-3).
|
|
@@ -13,12 +16,14 @@
|
|
|
13
16
|
import { RenderError } from '../../errors.js';
|
|
14
17
|
import type { Graph } from '../../model/graph.js';
|
|
15
18
|
import { srand48 } from '../../common/random.js';
|
|
19
|
+
import { registerRenderState } from '../../common/render-state.js';
|
|
16
20
|
import { mapbool, isACluster } from '../dot/rank.js';
|
|
17
21
|
import { getPack } from '../pack/index.js';
|
|
18
22
|
import {
|
|
19
23
|
MODE_KK, MODE_MAJOR, MODE_HIER, MODE_IPSEP, MODE_SGD,
|
|
20
24
|
MODEL_CIRCUIT, MODEL_SUBSET,
|
|
21
25
|
} from './init.js';
|
|
26
|
+
import { agerrLineOn, agwarningf } from '../../common/agerr.js';
|
|
22
27
|
|
|
23
28
|
/** @see lib/neatogen/neato.h:INIT_SELF */
|
|
24
29
|
export const INIT_SELF = 0;
|
|
@@ -41,11 +46,12 @@ const WARN_POS_IGNORED = 'node positions are ignored unless start=random';
|
|
|
41
46
|
const WARN_SELF_IGNORED = 'start=0 not supported with mode=self - ignored';
|
|
42
47
|
|
|
43
48
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
49
|
+
* C's `static atomic_flag once` for the start=self warning under KK/SGD: once
|
|
50
|
+
* per process, so once per render here (AD-4 resets it at render entry).
|
|
51
|
+
* @see lib/neatogen/stuff.c:330
|
|
47
52
|
*/
|
|
48
|
-
const
|
|
53
|
+
const selfOnce = { warned: false };
|
|
54
|
+
registerRenderState(() => { selfOnce.warned = false; });
|
|
49
55
|
/** Roots already warned about the KK circuit fallback (one warning per graph). */
|
|
50
56
|
const circuitWarned = new WeakSet<object>();
|
|
51
57
|
|
|
@@ -96,7 +102,7 @@ export function checkStart(
|
|
|
96
102
|
): number {
|
|
97
103
|
const { init, seed } = parseStart(g.root.attrs.get('start'), dflt);
|
|
98
104
|
if (init !== INIT_RANDOM && [...g.nodes.values()].some((n) => n.attrs.has('pos'))) {
|
|
99
|
-
|
|
105
|
+
agerrLineOn('warn', WARN_POS_IGNORED);
|
|
100
106
|
}
|
|
101
107
|
if (init === INIT_REGULAR) initRegular(g, nG);
|
|
102
108
|
srand48(seed);
|
|
@@ -141,14 +147,12 @@ export function neatoMode(g: Graph): number {
|
|
|
141
147
|
if (str === undefined || str === '') return MODE_MAJOR;
|
|
142
148
|
const mode = modeByName().get(str);
|
|
143
149
|
if (mode !== undefined) return mode;
|
|
144
|
-
|
|
150
|
+
agwarningf(`Illegal value ${str} for attribute "mode" in graph ${g.root.name} - ignored`);
|
|
145
151
|
return MODE_MAJOR;
|
|
146
152
|
}
|
|
147
153
|
|
|
148
154
|
function checkMode(g: Graph, mode: number): void {
|
|
149
|
-
const value = g.root.attrs.get('mode') ||
|
|
150
|
-
// @see lib/neatogen/neatoinit.c:1117 (stress_majorization_with_hierarchy)
|
|
151
|
-
if (mode === MODE_HIER) unsupported('mode', value, 'hierarchical stress majorization');
|
|
155
|
+
const value = g.root.attrs.get('mode') || 'ipsep';
|
|
152
156
|
// @see lib/neatogen/neatoinit.c:1121-1170 (stress_majorization_cola)
|
|
153
157
|
if (mode === MODE_IPSEP && ipsepHasConstraints(g)) {
|
|
154
158
|
unsupported('mode', value, 'constrained (ipsep) majorization');
|
|
@@ -161,9 +165,9 @@ function checkStartAttr(g: Graph, mode: number): void {
|
|
|
161
165
|
// @see lib/neatogen/neatoinit.c:1095, lib/neatogen/stress.c:884 (smart_ini)
|
|
162
166
|
if (mode === MODE_MAJOR || mode === MODE_IPSEP) unsupported('start', value ?? '', 'smart initialisation');
|
|
163
167
|
// @see lib/neatogen/stuff.c:330 (KK and SGD warn once and ignore it)
|
|
164
|
-
if ((mode === MODE_KK || mode === MODE_SGD) && !
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
if ((mode === MODE_KK || mode === MODE_SGD) && !selfOnce.warned) {
|
|
169
|
+
selfOnce.warned = true;
|
|
170
|
+
agerrLineOn('warn', WARN_SELF_IGNORED);
|
|
167
171
|
}
|
|
168
172
|
}
|
|
169
173
|
|
|
@@ -189,7 +193,8 @@ function kkCircuitFallsBack(g: Graph): boolean {
|
|
|
189
193
|
function warnCircuitDisconnected(g: Graph): void {
|
|
190
194
|
if (circuitWarned.has(g.root)) return;
|
|
191
195
|
circuitWarned.add(g.root);
|
|
192
|
-
|
|
196
|
+
agerrLineOn(
|
|
197
|
+
'warn',
|
|
193
198
|
`graph ${g.root.name} is disconnected. Hence, the circuit model\n`
|
|
194
199
|
+ 'is undefined. Reverting to the shortest path model.\n'
|
|
195
200
|
+ 'Alternatively, consider running neato using -Gpack=true or decomposing\n'
|
|
@@ -296,6 +296,34 @@ export class DijHelper {
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// mdsModel
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Packed shortest paths with the user-supplied edge lengths written over the
|
|
305
|
+
* entries of adjacent nodes; `null` (C `NULL`) when the graph carries no
|
|
306
|
+
* lengths (`graph->ewgts == NULL`, an empty `ewgts` here). C's `delta`
|
|
307
|
+
* accumulator is only printed under `Verbose` and is not ported.
|
|
308
|
+
* @see lib/neatogen/stress.c:mdsModel
|
|
309
|
+
*/
|
|
310
|
+
export function mdsModel(graph: VtxData[], nG: number): Float32Array | null {
|
|
311
|
+
if (graph[0]!.ewgts.length === 0) return null;
|
|
312
|
+
// first, compute shortest paths to fill in non-edges
|
|
313
|
+
const Dij = computeWeightedApspPacked(graph, nG);
|
|
314
|
+
// then, replace edge entries with user-supplied len
|
|
315
|
+
let shift = 0;
|
|
316
|
+
for (let i = 0; i < nG; i++) {
|
|
317
|
+
shift += i;
|
|
318
|
+
for (let e = 1; e < graph[i]!.nedges; e++) {
|
|
319
|
+
const j = graph[i]!.edges[e]!;
|
|
320
|
+
if (j < i) continue;
|
|
321
|
+
Dij[i * nG + j - shift] = graph[i]!.ewgts[e]!;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return Dij;
|
|
325
|
+
}
|
|
326
|
+
|
|
299
327
|
// ---------------------------------------------------------------------------
|
|
300
328
|
// CoordHelper — coordinate conversion and initialization
|
|
301
329
|
// ---------------------------------------------------------------------------
|
package/src/model/edge.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Node } from './node.js';
|
|
11
|
+
import { EMPTY_ATTRS } from './lazy-attrs.js';
|
|
11
12
|
import { type EdgeInfo, makeEdgeInfo, makePort } from './edgeInfo.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -93,12 +94,12 @@ export class Edge {
|
|
|
93
94
|
info: EdgeInfo;
|
|
94
95
|
|
|
95
96
|
/** @see lib/cgraph/edge.c:agedge */
|
|
96
|
-
constructor(tail: Node, head: Node, name: string) {
|
|
97
|
+
constructor(tail: Node, head: Node, name: string, lazyAttrs = false) {
|
|
97
98
|
this.seq = Edge._nextSeq++;
|
|
98
99
|
this.tail = tail;
|
|
99
100
|
this.head = head;
|
|
100
101
|
this.name = name;
|
|
101
|
-
this.attrs = new Map();
|
|
102
|
+
this.attrs = lazyAttrs ? EMPTY_ATTRS : new Map();
|
|
102
103
|
this.info = makeEdgeInfo(makePort(), makePort());
|
|
103
104
|
}
|
|
104
105
|
}
|